Neko 1.99.5
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
ale_manager.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!
35 use num_types, only : rp, dp
36 use json_module, only : json_file
38 use field, only : field_t
39 use coefs, only : coef_t
40 use space, only : space_t
41 use ax_product, only : ax_t, ax_helm_factory
42 use krylov, only : ksp_t, ksp_monitor_t, krylov_solver_factory
43 use precon, only : pc_t, precon_factory, precon_destroy
44 use bc_list, only : bc_list_t
45 use checkpoint, only : chkp_t
47 use gather_scatter, only : gs_t, gs_op_add
48 use dofmap, only : dofmap_t
49 use jacobi, only : jacobi_t
50 use hsmg, only : hsmg_t
51 use phmg, only : phmg_t
53 use sx_jacobi, only : sx_jacobi_t
55 use file, only : file_t
56 use logger, only : neko_log, log_size
57 use advection, only : advection_t
66 use utils, only : neko_error
68 use mpi_f08, only : mpi_wtime, mpi_barrier
69 use comm, only : neko_comm
70 use registry, only : neko_registry
73 use time_state, only : time_state_t
74 use fld_file, only : fld_file_t
79 use math, only : glmin, pi, copy
81 use field_math, only : field_rzero, field_add2, &
84 use operators, only : rotate_cyc
86 use, intrinsic :: iso_c_binding, only : c_associated
87 implicit none
88 private
89
90 public :: compute_stiffness_ale
92 public :: update_ale_mesh
93 public :: log_rot_angles
94 public :: log_pivot
95
96 type, public :: ale_manager_t
97 ! Default
98 logical :: active = .false.
99 logical :: has_moving_boundary = .false.
100
102 type(zero_dirichlet_t) :: bc_moving
103 type(zero_dirichlet_t) :: bc_fixed
104
105 type(ale_config_t) :: config
106
108 type(field_t), pointer :: wm_x => null()
109 type(field_t), pointer :: wm_y => null()
110 type(field_t), pointer :: wm_z => null()
111
113 type(field_series_t) :: wm_x_lag
114 type(field_series_t) :: wm_y_lag
115 type(field_series_t) :: wm_z_lag
116
118 type(field_t) :: x_ref, y_ref, z_ref
119
121 type(pivot_state_t), allocatable :: ale_pivot(:)
122 type(body_kinematics_t), allocatable :: body_kin(:)
123
126 type(field_t), allocatable :: base_shapes(:)
127
129 type(field_t) :: phi_total
130
131 real(kind=rp), pointer :: global_pivot_pos(:) => null()
132 real(kind=rp), pointer :: global_pivot_vel_lag(:, :) => null()
133
134 ! Basis Vectors for orientation
135 real(kind=rp), pointer :: global_basis_pos(:) => null()
136 ! Store history for the ghost trackers
137 real(kind=rp), pointer :: global_basis_vel_lag(:, :) => null()
138 ! Private handles to the ghost trackers (2 per body)
139 integer, allocatable :: ghost_handles(:,:)
140 ! Rotation matrices
141 real(kind=rp), allocatable :: body_rot_matrices(:,:,:)
142
143 type(point_tracker_t), allocatable :: trackers(:)
144 integer :: n_trackers = 0
145
146 procedure(user_ale_mesh_velocity_intf), nopass, pointer :: &
147 user_ale_mesh_vel => null()
148 procedure(user_ale_base_shapes_intf), nopass, pointer :: &
149 user_ale_base_shapes => null()
150 procedure(user_ale_rigid_kinematics_intf), nopass, pointer :: &
151 user_ale_rigid_kinematics => null()
152
153 contains
154 procedure, pass(this) :: init => ale_manager_init
155 procedure, pass(this) :: free => ale_manager_free
156 procedure, pass(this) :: mesh_preview
157 procedure, pass(this) :: solve_base_mesh_displacement
158 procedure, pass(this) :: advance_mesh
159 procedure, pass(this) :: update_mesh_velocity
160 procedure, pass(this) :: set_pivot_restart
161 procedure, pass(this) :: sync_chkp
162 procedure, pass(this) :: request_tracker
163 procedure, pass(this) :: get_tracker_pos
164 procedure, pass(this) :: compute_rotation_matrix
165 procedure, pass(this) :: prep_checkpoint => set_pivot_basis_for_checkpoint
166 procedure, pass(this) :: ghost_tracker_coord_step
167 procedure, pass(this) :: log_rot_angles
168 procedure, pass(this) :: log_pivot
169 procedure, pass(this) :: register_checkpoint_fields
170 end type ale_manager_t
171
172 type(ale_manager_t), public, pointer :: neko_ale => null()
173
174contains
175
178 subroutine ale_manager_init(this, coef, json, user, chkp)
179 class(ale_manager_t), intent(inout), target :: this
180 type(coef_t), intent(inout) :: coef
181 type(json_file), intent(inout) :: json
182 type(user_t), intent(in) :: user
183 type(chkp_t), intent(inout) :: chkp
184 type(json_file) :: body_sub, bc_subdict
185 type(json_file) :: precon_params
186 type(time_state_t) :: t_init
187 integer, allocatable :: zone_indices(:)
188 integer :: time_order
189 integer :: n_moving_zones
190 integer :: z, tmp_int, ksp_max_iter
191 integer, allocatable :: moving_zone_ids(:)
192 integer :: i, j, k, n_bcs, n, n_bodies
193 real(kind=rp), allocatable :: tmp_vec(:)
194 real(kind=rp) :: tmp_val, abstol
195 character(len=128) :: log_buf
196 character(len=256) :: log_buf_l
197 character(len=:), allocatable :: bc_type
198 character(len=:), allocatable :: tmp_str
199 character(len=:), allocatable :: ksp_solver
200 character(len=:), allocatable :: precon_type
201 logical :: tmp_logical, oifs
202 logical :: moving_
203 logical :: found_zone
204 logical :: has_user_rigid_kin, has_user_mesh_vel
205 logical :: has_builtin_osc, has_builtin_rot, is_rot_active
206 logical :: res_monitor, import_base_shapes
207
208 if (json%valid_path('case.fluid.ale')) then
209 call json_get(json, 'case.fluid.ale.enabled', this%active)
210 end if
211 call json_get_or_default(json, 'case.numerics.oifs', oifs, .false.)
212
213 if (.not. this%active) then
214 neko_ale => null()
215 return
216 else if (this%active) then
217 if (neko_bcknd_device .eq. 1) then
218 if ((.not. (neko_bcknd_hip .eq. 1)) .and. &
219 (.not. (neko_bcknd_cuda .eq. 1))) then
220 call neko_error("ALE currently " // &
221 "supported only with HIP or CUDA backend.")
222 end if
223 end if
224 if (oifs) then
225 call neko_error("ALE not currently supported with OIFS.")
226 end if
227 if (json%valid_path('case.checkpoint_format')) then
228 call json_get(json, 'case.checkpoint_format', tmp_str)
229 if (trim(tmp_str) /= 'chkp') then
230 call neko_error("ALE is not supported with the '" // &
231 trim(tmp_str) // &
232 "' checkpoint format. Please use 'chkp'.")
233 end if
234 end if
235 neko_ale => this
236 end if
237
238 call neko_log%section("ALE Initialization")
239 call neko_log%message(" ")
240
241 if (neko_bcknd_hip .eq. 1) then
242 call neko_log%message("Initializing ALE " // &
243 "with device backend (HIP).")
244 else if (neko_bcknd_cuda .eq. 1) then
245 call neko_log%message("Initializing ALE " // &
246 "with device backend (CUDA).")
247 else
248 call neko_log%message("Initializing ALE " // &
249 "with CPU backend.")
250 end if
251
252 tmp_logical = .false.
253 n = coef%dof%size()
254
255 call this%x_ref%init(coef%dof, "x_ref")
256 call this%y_ref%init(coef%dof, "y_ref")
257 call this%z_ref%init(coef%dof, "z_ref")
258
259 call copy(this%x_ref%x, coef%dof%x, n)
260 call copy(this%y_ref%x, coef%dof%y, n)
261 call copy(this%z_ref%x, coef%dof%z, n)
262
263 ! Sync to device
264 if (neko_bcknd_device .eq. 1) then
265 call this%x_ref%copy_from(host_to_device, .false.)
266 call this%y_ref%copy_from(host_to_device, .false.)
267 call this%z_ref%copy_from(host_to_device, .true.)
268 end if
269
270 ! Set user function pointers.
271 this%user_ale_mesh_vel => user%ale_mesh_velocity
272 this%user_ale_base_shapes => user%ale_base_shapes
273 this%user_ale_rigid_kinematics => user%ale_rigid_kinematics
274
275 ! Check user association states
276 has_user_rigid_kin = .not. associated(this%user_ale_rigid_kinematics, &
278 has_user_mesh_vel = .not. associated(this%user_ale_mesh_vel, &
280
281 ! Enable B history (Blag, Blaglag)
282 call coef%enable_B_history()
283 call json_get(json, 'case.numerics.time_order', time_order)
284
285 ! Stuff for zone_id checks
286 n_moving_zones = 0
287 if (allocated(moving_zone_ids)) deallocate(moving_zone_ids)
288 allocate(moving_zone_ids(0))
289
290 ! Register mesh velocity fields
291 call neko_registry%add_field(coef%dof, 'wm_x')
292 call neko_registry%add_field(coef%dof, 'wm_y')
293 call neko_registry%add_field(coef%dof, 'wm_z')
294 this%wm_x => neko_registry%get_field('wm_x')
295 this%wm_y => neko_registry%get_field('wm_y')
296 this%wm_z => neko_registry%get_field('wm_z')
297
298 call get_ale_solver_params_json(this, json, ksp_solver, precon_type, &
299 precon_params, abstol, ksp_max_iter, res_monitor, import_base_shapes)
300
301 ! Mark BCs
302 call this%bc_moving%init_from_components(coef)
303 call this%bc_fixed%init_from_components(coef)
304
305 if (json%valid_path('case.fluid.boundary_conditions')) then
306 call json%info('case.fluid.boundary_conditions', n_children = n_bcs)
307
308 do i = 1, n_bcs
309 call json_extract_item(json, 'case.fluid.boundary_conditions', &
310 i, bc_subdict)
311
312 if (allocated(bc_type)) deallocate(bc_type)
313 call json_get(bc_subdict, 'type', bc_type)
314
315 if (allocated(zone_indices)) deallocate(zone_indices)
316 call json_get(bc_subdict, 'zone_indices', zone_indices)
317
318 moving_ = .false.
319 if (trim(bc_type) .eq. 'no_slip') then
320 call json_get_or_default(bc_subdict, 'moving', moving_, .false.)
321 end if
322
323 if (moving_) then
324 do j = 1, size(zone_indices)
325 ! we append unique moving zone ids for future checks
326 call append_unique_int(moving_zone_ids, n_moving_zones, &
327 zone_indices(j))
328 call this%bc_moving%mark_zone(coef%msh%labeled_zones(&
329 zone_indices(j)))
330 end do
331 this%has_moving_boundary = .true.
332 else
333 do j = 1, size(zone_indices)
334 call this%bc_fixed%mark_zone(coef%msh%labeled_zones(&
335 zone_indices(j)))
336 end do
337 end if
338 end do
339 end if
340
341 call this%bc_moving%finalize()
342 call this%bc_fixed%finalize()
343 call this%bc_list%init()
344 call this%bc_list%append(this%bc_moving)
345 call this%bc_list%append(this%bc_fixed)
346
347 ! Mesh Stiffness
348 if (json%valid_path('case.fluid.ale.solver.mesh_stiffness.type')) then
349 call json%get('case.fluid.ale.solver.mesh_stiffness.type', tmp_str)
350 this%config%stiffness_type = tmp_str
351 if (.not. (trim(tmp_str) .eq. 'built-in')) then
352 call neko_error("ALE: stiffness_type must be 'built-in'")
353 end if
354 end if
355
356 if ( associated(this%user_ale_base_shapes, &
357 dummy_user_ale_base_shapes) .and. (.not. import_base_shapes)) then
358 call neko_log%message('Solver Type : (' // &
359 trim(ksp_solver) // ', ' // trim(precon_type) // ')')
360 write(log_buf, '(A,ES13.6)') 'Abs tol :', abstol
361 call neko_log%message(log_buf)
362 call neko_log%message('Mesh Stiffness : ' // &
363 trim(this%config%stiffness_type))
364 end if
365 call neko_log%message(' ')
366
367 ! Bodies
368 if (json%valid_path('case.fluid.ale.bodies')) then
369 call json%info('case.fluid.ale.bodies', n_children = n_bodies)
370 this%config%nbodies = n_bodies
371 allocate(this%config%bodies(n_bodies))
372 allocate(this%ale_pivot(n_bodies))
373 allocate(this%body_kin(n_bodies))
374 allocate(this%base_shapes(n_bodies))
375 allocate(this%global_pivot_pos(3 * this%config%nbodies))
376 allocate(this%global_pivot_vel_lag(3 * this%config%nbodies, 3))
377 allocate(this%global_basis_pos(6 * this%config%nbodies))
378 allocate(this%ghost_handles(2, this%config%nbodies))
379 allocate(this%global_basis_vel_lag(6 * this%config%nbodies, 3))
380 allocate(this%body_rot_matrices(3, 3, this%config%nbodies))
381
382 this%global_pivot_pos = 0.0_rp
383 this%global_pivot_vel_lag = 0.0_rp
384 this%global_basis_pos = 0.0_rp
385 this%global_basis_vel_lag = 0.0_rp
386 this%body_rot_matrices = 0.0_rp
387
388 do i = 1, n_bodies
389 this%body_rot_matrices(1, 1, i) = 1.0_rp
390 this%body_rot_matrices(2, 2, i) = 1.0_rp
391 this%body_rot_matrices(3, 3, i) = 1.0_rp
392 end do
393
394 do i = 1, n_bodies
395 call json_extract_item(json, 'case.fluid.ale.bodies', i, body_sub)
396 this%config%bodies(i)%id = i
397
398 if (body_sub%valid_path('name')) then
399 call json_get(body_sub, 'name', tmp_str)
400 this%config%bodies(i)%name = tmp_str
401 else
402 write(this%config%bodies(i)%name, '(A,I0)') 'body_', i
403 endif
404
405 if (body_sub%valid_path('zone_indices')) then
406 call json_get(body_sub, 'zone_indices', zone_indices)
407 this%config%bodies(i)%zone_indices = zone_indices
408 else
409 call neko_error("ALE: body " // &
410 trim(this%config%bodies(i)%name) // &
411 " must have 'zone_indices'")
412 endif
413
414 ! Oscillation
415 this%config%bodies(i)%osc_amp = 0.0_rp
416 this%config%bodies(i)%osc_freq = 0.0_rp
417 if (body_sub%valid_path('oscillation')) then
418 call json_get(body_sub, 'oscillation.amplitude', tmp_vec, &
419 expected_size = 3)
420 this%config%bodies(i)%osc_amp = tmp_vec
421 call json_get(body_sub, 'oscillation.frequency', tmp_vec, &
422 expected_size = 3)
423 this%config%bodies(i)%osc_freq = tmp_vec
424 end if
425
426 ! Rotation
427 if (body_sub%valid_path('rotation')) then
428 ! Check if pivot exists.
429 if (.not. body_sub%valid_path('pivot')) then
430 call neko_error("ale.bodies.pivot is missing " // &
431 "from the case file.")
432 end if
433
434 call json_get(body_sub, 'rotation.type', tmp_str)
435 this%config%bodies(i)%rotation_type = tmp_str
436
437 select case (trim(tmp_str))
438 case ('harmonic')
439 call json_get(body_sub, 'rotation.amplitude_deg', tmp_vec, &
440 expected_size = 3)
441 this%config%bodies(i)%rot_amp_degree = tmp_vec
442
443 call json_get(body_sub, 'rotation.frequency', tmp_vec, &
444 expected_size = 3)
445 this%config%bodies(i)%rot_freq = tmp_vec
446
447
448 case ('ramp')
449 call json_get(body_sub, 'rotation.ramp_t0', tmp_vec, &
450 expected_size = 3)
451 this%config%bodies(i)%ramp_t0 = tmp_vec
452
453 call json_get(body_sub, 'rotation.ramp_omega0', tmp_vec, &
454 expected_size = 3)
455 this%config%bodies(i)%ramp_omega0 = tmp_vec
456
457
458 case ('smooth_step')
459 call json_get_or_default(body_sub, 'rotation.axis', &
460 tmp_int, 3)
461 if (tmp_int .ge. 1 .and. tmp_int .le. 3) then
462 this%config%bodies(i)%rotation_axis = tmp_int
463 else
464 call neko_error("ALE: rotation.axis must be (integer) " // &
465 "1 -> x, 2 -> y, or 3 -> z")
466 end if
467 call json_get(body_sub, 'rotation.step_control_times', &
468 tmp_vec, expected_size = 4)
469 this%config%bodies(i)%step_control_times = tmp_vec
470
471 call json_get(body_sub, 'rotation.target_angle_deg', tmp_val)
472 this%config%bodies(i)%target_rot_angle_deg = tmp_val
473
474 case default
475 call neko_error("ALE: rotation.type must be 'harmonic', " // &
476 "'ramp', or 'smooth_step'")
477 end select
478 end if
479
480 ! Rotation Center
481 if (body_sub%valid_path('pivot')) then
482 call json_get_or_default(body_sub, 'pivot.type', tmp_str, &
483 'relative')
484 this%config%bodies(i)%rotation_center_type = tmp_str
485 call json_get(body_sub, 'pivot.value', tmp_vec, expected_size = 3)
486 this%config%bodies(i)%rot_center = tmp_vec
487
488
489 tmp_str = this%config%bodies(i)%rotation_center_type
490 if (trim(tmp_str) /= 'relative' .and. &
491 trim(tmp_str) /= 'relative_sin') then
492 call neko_error("ALE: pivot.type must be " // &
493 "'relative', or 'relative_sin'.")
494 end if
495 end if
496
497 ! Stiff Geom
498 if (body_sub%valid_path('stiff_geom')) then
499 call json_get(body_sub, 'stiff_geom.type', tmp_str)
500 this%config%bodies(i)%stiff_geom%type = tmp_str
501 call json_get(body_sub, 'stiff_geom.gain', &
502 this%config%bodies(i)%stiff_geom%gain)
503 call json_get(body_sub, 'stiff_geom.decay_profile', tmp_str)
504 this%config%bodies(i)%stiff_geom%decay_profile = tmp_str
505
506 select case (trim(this%config%bodies(i)%stiff_geom%decay_profile))
507 case ('gaussian')
508 call json_get_or_default(body_sub, &
509 'stiff_geom.cutoff_coef', &
510 this%config%bodies(i)%stiff_geom%cutoff_coef, 9.0_rp)
511 case ('tanh')
512 call json_get_or_default(body_sub, &
513 'stiff_geom.cutoff_coef', &
514 this%config%bodies(i)%stiff_geom%cutoff_coef, 3.5_rp)
515 case default
516 call neko_error("ALE: Invalid stiff_geom.decay_profile: " // &
517 trim(this%config%bodies(i)%stiff_geom%decay_profile))
518 end select
519
520 select case (trim(this%config%bodies(i)%stiff_geom%type))
521 case ('cylinder', 'sphere')
522 call json_get(body_sub, 'stiff_geom.center', tmp_vec, &
523 expected_size = 3)
524 this%config%bodies(i)%stiff_geom%center = tmp_vec
525
526 call json_get(body_sub, 'stiff_geom.radius', &
527 this%config%bodies(i)%stiff_geom%radius)
528 case ('cheap_dist')
529 call json_get(body_sub, 'stiff_geom.stiff_dist', &
530 this%config%bodies(i)%stiff_geom%stiff_dist)
531 case ('box')
532 call neko_error("ALE: stiff_geom.type 'box' not yet" // &
533 " implemented.")
534 case default
535 call neko_error("ALE: Invalid stiff_geom.type: " // &
536 trim(this%config%bodies(i)%stiff_geom%type))
537 end select
538 elseif (import_base_shapes) then
539 ! do nothing.
540 else
541 call neko_error("ALE: Body '" // &
542 trim(this%config%bodies(i)%name) // &
543 "' must have 'stiff_geom' definition.")
544 end if
545
546 ! Initialize the pivots.
547 call init_pivot_state(this%ale_pivot(i), this%config%bodies(i))
548
549 call this%base_shapes(i)%init(coef%dof, &
550 "phi_" // trim(this%config%bodies(i)%name))
551 call field_rzero(this%base_shapes(i))
552
553 ! Create Ghost Trackers for numerically forming the rotation matrix
554 ! of each body.
555 ! Basis X (Pivot + 1.0 in X)
556 this%ghost_handles(1, i) = this%request_tracker( &
557 this%config%bodies(i)%rot_center + [1.0_rp, 0.0_rp, 0.0_rp], &
558 this%config%bodies(i)%id)
559 ! Basis Y (Pivot + 1.0 in Y)
560 this%ghost_handles(2, i) = this%request_tracker( &
561 this%config%bodies(i)%rot_center + [0.0_rp, 1.0_rp, 0.0_rp], &
562 this%config%bodies(i)%id)
563
564 call neko_log%message('Registered Body : ' // &
565 trim(this%config%bodies(i)%name))
566
567 ! Logging Stiff Body
568 call neko_log%message(' ')
569 if (associated(this%user_ale_base_shapes, &
571 (.not. import_base_shapes)) then
572 write(log_buf, '(A,A)') ' Stiff Type : ', &
573 trim(this%config%bodies(i)%stiff_geom%type)
574 call neko_log%message(log_buf)
575 write(log_buf, '(A,ES18.11,A,A,A,ES10.4)') ' Gain : ', &
576 this%config%bodies(i)%stiff_geom%gain, ' | Profile: ', &
577 trim(this%config%bodies(i)%stiff_geom%decay_profile), &
578 ' | Cutoff: ', this%config%bodies(i)%stiff_geom%cutoff_coef
579 call neko_log%message(log_buf)
580 select case (trim(this%config%bodies(i)%stiff_geom%type))
581 case ('cylinder', 'sphere')
582 write(log_buf, '(A,3(ES23.15,1X))') ' Center :', &
583 this%config%bodies(i)%stiff_geom%center
584 call neko_log%message(log_buf)
585 write(log_buf, '(A,ES23.15)') ' Radius :', &
586 this%config%bodies(i)%stiff_geom%radius
587 call neko_log%message(log_buf)
588 case ('cheap_dist')
589 write(log_buf, '(A,ES23.15)') ' Stiff Dist:', &
590 this%config%bodies(i)%stiff_geom%stiff_dist
591 call neko_log%message(log_buf)
592 end select
593 end if
594 call neko_log%message(' ')
595
596 ! Logging Oscillation
597 has_builtin_osc = any(abs(this%config%bodies(i)%osc_amp) .gt. 0.0_rp)
598
599 if (has_builtin_osc) then
600 if (has_user_rigid_kin .or. has_user_mesh_vel) then
601 call neko_log%message(' Oscillation : ' // &
602 'X(t) = Amp*sin(2*pi*Freq*t) + User')
603 write(log_buf, '(A,3(ES18.11,1X))') ' Amp :', &
604 this%config%bodies(i)%osc_amp
605 call neko_log%message(log_buf)
606 write(log_buf, '(A,3(ES18.11,1X))') ' Freq :', &
607 this%config%bodies(i)%osc_freq
608 call neko_log%message(log_buf)
609 else
610 call neko_log%message(' Oscillation : ' // &
611 'X(t) = Amp*sin(2*pi*Freq*t)')
612 write(log_buf, '(A,3(ES18.11,1X))') ' Amp :', &
613 this%config%bodies(i)%osc_amp
614 call neko_log%message(log_buf)
615 write(log_buf, '(A,3(ES18.11,1X))') ' Freq :', &
616 this%config%bodies(i)%osc_freq
617 call neko_log%message(log_buf)
618 end if
619 else
620 if (has_user_rigid_kin .or. has_user_mesh_vel) then
621 call neko_log%message(' Oscillation : User-defined')
622 else
623 call neko_log%message(' Oscillation : None')
624 end if
625 end if
626 call neko_log%message(' ')
627
628 ! Logging Rotation
629 has_builtin_rot = (trim(this%config%bodies(i)%rotation_type) &
630 /= 'user')
631
632 if (trim(this%config%bodies(i)%rotation_type) .eq. 'user') then
633
634 call neko_log%message(' Rotation Type: User-defined')
635
636 elseif (has_builtin_rot) then
637
638 ! Check parameters active
639 is_rot_active = .false.
640 select case (trim(this%config%bodies(i)%rotation_type))
641 case ('harmonic')
642 is_rot_active = any(abs(this%config%bodies(i)%rot_amp_degree) &
643 .gt. 0.0_rp)
644 case ('ramp')
645 is_rot_active = any(abs(this%config%bodies(i)%ramp_omega0) &
646 .gt. 0.0_rp)
647 case ('smooth_step')
648 is_rot_active = &
649 (abs(this%config%bodies(i)%target_rot_angle_deg) &
650 .gt. 0.0_rp)
651 end select
652
653 if (is_rot_active) then
654 ! Harmonic
655 if (trim(this%config%bodies(i)%rotation_type) &
656 .eq. 'harmonic') then
657 if (has_user_rigid_kin .or. has_user_mesh_vel) then
658 call neko_log%message(' Rotation : ' // &
659 'Theta(t) = Amp*sin(2*pi*Freq*t) + User')
660 else
661 call neko_log%message(' Rotation : ' // &
662 'Theta(t) = Amp*sin(2*pi*Freq*t)')
663 end if
664 write(log_buf, '(A,3(ES18.11,1X))') ' Amp (deg) :', &
665 this%config%bodies(i)%rot_amp_degree
666 call neko_log%message(log_buf)
667 write(log_buf, '(A,3(ES18.11,1X))') ' Freq :', &
668 this%config%bodies(i)%rot_freq
669 call neko_log%message(log_buf)
670
671 ! Ramp
672 elseif (trim(this%config%bodies(i)%rotation_type) &
673 .eq. 'ramp') then
674 if (has_user_rigid_kin .or. has_user_mesh_vel) then
675 call neko_log%message(' Rotation : ' // &
676 'Omega(t) = Omega0*(1 - exp(-4.6*t/t0)) + User')
677 else
678 call neko_log%message(' Rotation : ' // &
679 'Omega(t) = Omega0*(1 - exp(-4.6*t/t0))')
680 end if
681 write(log_buf, '(A,3(ES18.11,1X))') ' Omega0 :', &
682 this%config%bodies(i)%ramp_omega0
683 call neko_log%message(log_buf)
684 write(log_buf, '(A,3(ES18.11,1X))') ' t0 :', &
685 this%config%bodies(i)%ramp_t0
686 call neko_log%message(log_buf)
687
688 ! Smooth Step
689 elseif (trim(this%config%bodies(i)%rotation_type) &
690 .eq. 'smooth_step') then
691 if (has_user_rigid_kin .or. has_user_mesh_vel) then
692 call neko_log%message(' Rotation : ' // &
693 'Smooth Step Control + User')
694 else
695 call neko_log%message(' Rotation : ' // &
696 'Smooth Step Control')
697 end if
698 write(log_buf, '(A,I10)') ' Rotation Axis :', &
699 this%config%bodies(i)%rotation_axis
700 call neko_log%message(log_buf)
701 write(log_buf, '(A,ES18.11)') ' Target Rot ' // &
702 'Angle (deg) :', &
703 this%config%bodies(i)%target_rot_angle_deg
704 call neko_log%message(log_buf)
705 write(log_buf, '(A,4(ES18.11,1X))') &
706 ' Control Times [t0, t1, t2, t3] :', &
707 this%config%bodies(i)%step_control_times
708 call neko_log%message(log_buf)
709 end if
710 else
711 if (has_user_rigid_kin .or. has_user_mesh_vel) then
712 call neko_log%message(' Rotation Type: User-defined')
713 else
714 call neko_log%message(' Rotation Type: None')
715 end if
716 end if
717
718 end if
719
720 ! Logging Pivot
721 call neko_log%message(' ')
722 call neko_log%message(' Pivot Type : ' // &
723 trim(this%config%bodies(i)%rotation_center_type))
724
725 write(log_buf, '(A,3(ES18.11,1X))') ' Init Pivot:', &
726 this%config%bodies(i)%rot_center
727 call neko_log%message(log_buf)
728 call neko_log%message(' ')
729
730 end do
731 else
732 call neko_error("ALE: No 'ale bodies' found in case file!")
733 end if
734
735 if (this%config%nbodies .gt. 1 .and. (.not. import_base_shapes)) then
736 call this%phi_total%init(coef%dof, "phi_total")
737 call field_rzero(this%phi_total)
738 end if
739
740 ! Check to be sure moving no_slip ids belong to an ALE body
741 do i = 1, n_moving_zones
742 z = moving_zone_ids(i)
743 found_zone = .false.
744 j = 1
745 do while ((.not. found_zone) .and. (j .le. this%config%nbodies))
746 if (any(this%config%bodies(j)%zone_indices .eq. z)) then
747 found_zone = .true.
748 end if
749 j = j + 1
750 end do
751 if (.not. found_zone) then
752 write(log_buf_l, '(A,I0,A)') &
753 "ALE: zone index ", z, &
754 " has BC no_slip with moving: true, " // &
755 "but it is not registered in ALE bodies."
756 call neko_error(trim(log_buf_l))
757 end if
758 end do
759
760 ! Any id registered in ALE bodies must have
761 ! no_slip with moving: true in BCs.
762 do j = 1, this%config%nbodies
763 if (allocated(this%config%bodies(j)%zone_indices)) then
764 do i = 1, size(this%config%bodies(j)%zone_indices)
765 z = this%config%bodies(j)%zone_indices(i)
766 found_zone = .false.
767 if (n_moving_zones .gt. 0) then
768 if (any(moving_zone_ids(1:n_moving_zones) .eq. z)) then
769 found_zone = .true.
770 end if
771 end if
772 if (.not. found_zone) then
773 write(log_buf_l, '(A,I0,A,A)') &
774 "ALE: zone index ", z, &
775 " is registered in ALE bodies, ", &
776 "but the BC is not no_slip with moving: true."
777 call neko_error(trim(log_buf_l))
778 end if
779 end do
780 end if
781 end do
782
783 ! Check no zone ID is assigned to more than one ALE body.
784 do j = 1, this%config%nbodies
785 if (allocated(this%config%bodies(j)%zone_indices)) then
786 do i = 1, size(this%config%bodies(j)%zone_indices)
787 z = this%config%bodies(j)%zone_indices(i)
788
789 do k = j + 1, this%config%nbodies
790 if (allocated(this%config%bodies(k)%zone_indices)) then
791 if (any(this%config%bodies(k)%zone_indices .eq. z)) then
792 write(log_buf_l, '(A,I0,A,A,A,A,A)') &
793 "ALE: zone index ", z, &
794 " is assigned to multiple bodies ('", &
795 trim(this%config%bodies(j)%name), "' and '", &
796 trim(this%config%bodies(k)%name), "')."
797 call neko_error(trim(log_buf_l))
798 end if
799 end if
800 end do
801
802 end do
803 end if
804 end do
805
806 ! Find the smooth blending function for mesh displacement.
807 call this%solve_base_mesh_displacement(coef, json, import_base_shapes, &
808 abstol, ksp_solver, ksp_max_iter, &
809 precon_type, precon_params, res_monitor)
810
811 ! If we are restarting, we skip this. It will be handled
812 ! properly by chkp file.
813 if (.not. json%valid_path('case.restart_file')) then
814 t_init%t = 0.0_rp
815 t_init%tstep = 0
816 t_init%dt = 0.0_rp
817 call this%update_mesh_velocity(coef, t_init)
818 end if
819
820 call this%wm_x_lag%init(this%wm_x, time_order)
821 call this%wm_y_lag%init(this%wm_y, time_order)
822 call this%wm_z_lag%init(this%wm_z, time_order)
823 do i = 1, time_order
824 call field_rzero(this%wm_x_lag%lf(i))
825 call field_rzero(this%wm_y_lag%lf(i))
826 call field_rzero(this%wm_z_lag%lf(i))
827 end do
828
829 if (allocated(moving_zone_ids)) deallocate(moving_zone_ids)
830 if (allocated(bc_type)) deallocate(bc_type)
831 if (allocated(zone_indices)) deallocate(zone_indices)
832 if (allocated(ksp_solver)) deallocate(ksp_solver)
833 if (allocated(precon_type)) deallocate(precon_type)
834 if (allocated(tmp_str)) deallocate(tmp_str)
835 if (allocated(tmp_vec)) deallocate(tmp_vec)
836
837 ! Performing mesh_preview.
838 call this%mesh_preview(coef, json)
839
840 ! Register checkpoint fields
841 call this%register_checkpoint_fields(coef, chkp)
842
843 call neko_log%end_section()
844 end subroutine ale_manager_init
845
851 subroutine solve_base_mesh_displacement(this, coef, json, &
852 import_base_shapes, abstol, ksp_solver, ksp_max_iter, precon_type, &
853 precon_params, res_monitor)
854 class(ale_manager_t), intent(inout), target :: this
855 class(ax_t), allocatable :: Ax
856 class(ksp_t), allocatable :: ksp
857 class(pc_t), allocatable :: pc
858 type(coef_t), intent(inout) :: coef
859 type(json_file), intent(inout) :: json
860 logical, intent(in) :: import_base_shapes
861 real(kind=rp), intent(in) :: abstol
862 logical, intent(in) :: res_monitor
863 character(len=*), intent(in) :: ksp_solver, precon_type
864 integer, intent(in) :: ksp_max_iter
865 type(json_file), intent(inout) :: precon_params
866 type(file_t) :: phi_file
867 type(field_t), pointer :: phi_ptr => null()
868 type(field_t) :: rhs_field
869 type(field_t) :: corr_field
870 type(ksp_monitor_t) :: monitor(1)
871 real(kind=rp) :: sample_start_time, sample_end_time
872 real(kind=rp) :: sample_time
873 character(len=LOG_SIZE) :: log_buf
874 integer :: n, i, m, k, ierr, body_idx, z_idx
875 integer :: j
876 real(kind=rp), allocatable :: h1_restore(:, :, :, :)
877 real(kind=rp), allocatable :: h2_restore(:, :, :, :)
878 type(zero_dirichlet_t) :: bc_active_body
879 type(zero_dirichlet_t) :: bc_inactive_body
880 type(bc_list_t) :: bcloc
881 type(bc_list_t) :: bcloc_zeros_only
882 type(json_file) :: body_sub
883 character(len=256) :: phi_fname
884 character(len=:), allocatable :: tmp_str
885
886
887 if (.not. this%active) return
888 if (.not. this%has_moving_boundary) return
889 if (this%config%nbodies .eq. 0) return
890
891 if (import_base_shapes) then
892 call neko_log%message(" ")
893 call neko_log%message("Importing ALE base shapes" // &
894 " (skipping Laplace solve)...")
895
896 do body_idx = 1, this%config%nbodies
897
898 call json_extract_item(json, 'case.fluid.ale.bodies', &
899 body_idx, body_sub)
900
901 call json_get(body_sub, 'base_shape_import_file', tmp_str)
902 phi_fname = tmp_str
903
904 phi_ptr => this%base_shapes(body_idx)
905
906 ! Load the field
907 call import_fields(fname = trim(phi_fname), p = phi_ptr)
908
909 call neko_log%message(" Loaded: " // &
910 trim(phi_fname) // &
911 " for body: " // &
912 trim(this%config%bodies(body_idx)%name))
913 end do
914
915 return
916 end if
917
918 call neko_log%message(" ")
919 call neko_log%message("Starting base mesh motion solve ...")
920 n = coef%dof%size()
921
922 call ax_helm_factory(ax, full_formulation = .false.)
923 call krylov_solver_factory(ksp, n, ksp_solver, &
924 ksp_max_iter, abstol, monitor = res_monitor)
925 call ale_precon_factory(pc, ksp, coef, coef%dof, &
926 coef%gs_h, this%bc_list, precon_type, precon_params)
927
928 ! Save original h1/h2
929 h1_restore = coef%h1
930 h2_restore = coef%h2
931
932 call rhs_field%init(coef%dof)
933 call corr_field%init(coef%dof)
934
935
936 ! User Defined Base Shapes (Skip Solver).
937 if (.not. associated(this%user_ale_base_shapes, &
939 call neko_log%message(" Using user-defined base shapes " // &
940 "(skipping Laplace solve)")
941
942 ! Call User Hook (Populates this%base_shapes)
943 call this%user_ale_base_shapes(this%base_shapes)
944
945 ! Compute phi_total (Sum of all user shapes)
946 if (this%config%nbodies .gt. 1) then
947 call field_rzero(this%phi_total)
948 do body_idx = 1, this%config%nbodies
949 call field_add2(this%phi_total, this%base_shapes(body_idx), n)
950 end do
951 end if
952
953 ! Output Shapes
954 if (this%config%if_output_phi) then
955 ! Individual Bodies
956 do body_idx = 1, this%config%nbodies
957 call phi_file%init('phi_' // &
958 trim(this%config%bodies(body_idx)%name) // '.fld', &
959 precision = rp)
960 select type (ft => phi_file%file_type)
961 type is (fld_file_t)
962 ft%skip_pressure = .false.
963 end select
964 call phi_file%write(this%base_shapes(body_idx))
965 call phi_file%free()
966 call neko_log%message(' phi_' // &
967 trim(this%config%bodies(body_idx)%name) // '.fld saved.')
968 end do
969
970 ! Total
971 if (this%config%nbodies .gt. 1) then
972 call neko_log%message(" phi_total.fld saved.")
973 select type (ft => phi_file%file_type)
974 type is (fld_file_t)
975 ft%skip_pressure = .false.
976 end select
977 call phi_file%init('phi_total.fld', precision = rp)
978 call phi_file%write(this%phi_total)
979 call phi_file%free()
980 end if
981 end if
982 else
983 ! Standard Laplace Solve (Requires Stiffness)
984
985 ! Compute Stiffness
986 call compute_stiffness_ale(coef, this%config)
987
988 ! Output Stiffness if requested (for diagnostic)
989 if (this%config%if_output_stiffness) then
990 rhs_field%x = coef%h1
991 call phi_file%init('stiffness.fld')
992 call phi_file%write(rhs_field)
993 call phi_file%free()
994 call field_rzero(rhs_field)
995 end if
996
997 ! Loop over bodies and Solve Laplace
998 do body_idx = 1, this%config%nbodies
999 call mpi_barrier(neko_comm, ierr)
1000 sample_start_time = mpi_wtime()
1001 call neko_log%message(" Solving laplace for body: " // &
1002 trim(this%config%bodies(body_idx)%name))
1003
1004 call bc_active_body%init_from_components(coef)
1005 call bc_inactive_body%init_from_components(coef)
1006
1007 ! Mark zones
1008 do j = 1, size(this%config%bodies(body_idx)%zone_indices)
1009 z_idx = this%config%bodies(body_idx)%zone_indices(j)
1010 call bc_active_body%mark_zone(coef%msh%labeled_zones(z_idx))
1011 end do
1012
1013 do i = 1, this%config%nbodies
1014 if (i /= body_idx) then
1015 do j = 1, size(this%config%bodies(i)%zone_indices)
1016 z_idx = this%config%bodies(i)%zone_indices(j)
1017 call bc_inactive_body%mark_zone(&
1018 coef%msh%labeled_zones(z_idx))
1019 end do
1020 end if
1021 end do
1022
1023 call bc_active_body%finalize()
1024 call bc_inactive_body%finalize()
1025
1026 ! The Full list for the solver (Freeze everything to 0 correction)
1027 call bcloc%init()
1028 call bcloc%append(this%bc_fixed)
1029 call bcloc%append(bc_active_body)
1030 call bcloc%append(bc_inactive_body)
1031
1032 ! The "Zeros Only" list for the field (Reset other boundaries)
1033 call bcloc_zeros_only%init()
1034 call bcloc_zeros_only%append(this%bc_fixed)
1035 call bcloc_zeros_only%append(bc_inactive_body)
1036
1037 call field_rzero(this%base_shapes(body_idx))
1038 this%base_shapes(body_idx)%x = 0.0_rp
1039 rhs_field%x = 0.0_rp
1040 corr_field%x = 0.0_rp
1041 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1042 ! phi = phi_corr + phi_lifted!
1043 ! A*phi_corr = -A*phi_lifted !
1044 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1045
1046 ! Lift BC (Dirichlet = 1.0 on moving body)
1047 m = bc_active_body%msk(0)
1048 do i = 1, m
1049 k = bc_active_body%msk(i)
1050 this%base_shapes(body_idx)%x(k, 1, 1, 1) = 1.0_rp
1051 end do
1052
1053 if (neko_bcknd_device .eq. 1) then
1054 call device_memcpy(this%base_shapes(body_idx)%x, &
1055 this%base_shapes(body_idx)%x_d, n, host_to_device, .true.)
1056 end if
1057
1058 ! Apply Zeros to others.
1059 ! This ensures fixed walls and other bodies are 0.0,
1060 ! even if they share grid with a moving wall.
1061 call bcloc_zeros_only%apply_scalar(this%base_shapes(body_idx)%x, n)
1062
1063 ! Compute RHS: RHS = -A * Phi_lifted.
1064 ! The following is motivated by implementation in Nek5000.
1065 call ax%compute(rhs_field%x, this%base_shapes(body_idx)%x, &
1066 coef, coef%msh, coef%Xh)
1067 call field_cmult(rhs_field, -1.0_rp)
1068
1069 ! Here we use the FULL list to apply zero Dirichlet BC
1070 ! on all boundaries.
1071 call bcloc%apply_scalar(rhs_field%x, n)
1072 call coef%gs_h%op(rhs_field, gs_op_add)
1073
1074 ! Solve
1075 call field_rzero(corr_field)
1076 call pc%update()
1077 monitor(1) = ksp%solve(ax, corr_field, &
1078 rhs_field%x, n, coef, bcloc, coef%gs_h)
1079
1080 ! phi = phi_lifted + phi_corr
1081 call field_add2(this%base_shapes(body_idx), corr_field, n)
1082
1083 ! Update Total Phi
1084 ! phi_total should be between 0 and 1.
1085 if (this%config%nbodies .gt. 1) then
1086 call field_add2(this%phi_total, this%base_shapes(body_idx), n)
1087 end if
1088
1089 call mpi_barrier(neko_comm, ierr)
1090 sample_end_time = mpi_wtime()
1091 sample_time = sample_end_time - sample_start_time
1092 write(log_buf, '(A, A, A, ES11.4, A)') " Laplace solve for '", &
1093 trim(this%config%bodies(body_idx)%name), "' took ", &
1094 sample_time, " (s)"
1095
1096 call neko_log%message(log_buf)
1097
1098 call bc_active_body%free()
1099 call bc_inactive_body%free()
1100 call bcloc%free()
1101 call bcloc_zeros_only%free()
1102
1103 ! We let the host to also have the base_shapes so in
1104 ! user_ale_mesh_vel
1105 ! it would be easier in general to use it.
1106 if (neko_bcknd_device .eq. 1) then
1107 call device_memcpy(this%base_shapes(body_idx)%x, &
1108 this%base_shapes(body_idx)%x_d, n, device_to_host, .true.)
1109 end if
1110
1111 if (this%config%if_output_phi) then
1112 call phi_file%init('phi_' // &
1113 trim(this%config%bodies(body_idx)%name) // '.fld', &
1114 precision = rp)
1115 select type (ft => phi_file%file_type)
1116 type is (fld_file_t)
1117 ft%skip_pressure = .false.
1118 end select
1119 call phi_file%write(this%base_shapes(body_idx))
1120 call phi_file%free()
1121 call neko_log%message(' phi_' // &
1122 trim(this%config%bodies(body_idx)%name) // '.fld saved.')
1123 end if
1124 end do
1125
1126 if (this%config%if_output_phi .and. (this%config%nbodies .gt. 1)) then
1127
1128 if (neko_bcknd_device .eq. 1) then
1129 call device_memcpy(this%phi_total%x, this%phi_total%x_d, n, &
1130 device_to_host, .true.)
1131 end if
1132
1133 call neko_log%message(" phi_total.fld saved.")
1134 call phi_file%init('phi_total.fld', precision = rp)
1135 select type (ft => phi_file%file_type)
1136 type is (fld_file_t)
1137 ft%skip_pressure = .false.
1138 end select
1139 call phi_file%write(this%phi_total)
1140 call phi_file%free()
1141 end if
1142 end if
1143
1144 call rhs_field%free()
1145 call corr_field%free()
1146 if (this%config%nbodies > 1) then
1147 call this%phi_total%free()
1148 end if
1149
1150 ! Restore h1/h2 to what they were before
1151 coef%h1(:,:,:,:) = h1_restore(:,:,:,:)
1152 coef%h2(:,:,:,:) = h2_restore(:,:,:,:)
1153 if (neko_bcknd_device .eq. 1) then
1154 call device_memcpy(coef%h1, coef%h1_d, n, host_to_device, .false.)
1155 call device_memcpy(coef%h2, coef%h2_d, n, host_to_device, .true.)
1156 end if
1157
1158 if (allocated(h1_restore)) deallocate(h1_restore)
1159 if (allocated(h2_restore)) deallocate(h2_restore)
1160 if (allocated(ax)) deallocate(ax)
1161 if (allocated(ksp)) then
1162 call ksp%free()
1163 deallocate(ksp)
1164 end if
1165 if (allocated(pc)) then
1166 call precon_destroy(pc)
1167 deallocate(pc)
1168 end if
1169
1170 end subroutine solve_base_mesh_displacement
1171
1174 subroutine update_mesh_velocity(this, coef, time_s)
1175 class(ale_manager_t), intent(inout) :: this
1176 type(coef_t), intent(in) :: coef
1177 type(time_state_t), intent(in) :: time_s
1178 integer :: i, n
1179 type(body_kinematics_t) :: current_kin
1180 real(kind=rp) :: rot_mat(3,3)
1181 real(kind=rp) :: initial_rot_center(3)
1182
1183 if (.not. this%active) return
1184 if (.not. this%has_moving_boundary) return
1185 call profiler_start_region('ALE add mesh velocity')
1186
1187 call field_rzero(this%wm_x)
1188 call field_rzero(this%wm_y)
1189 call field_rzero(this%wm_z)
1190
1191 do i = 1, this%config%nbodies
1192 ! Compute kinematics for built-in motions
1193 ! "current_kin" will be like solid body kinematics at current time
1194 call compute_body_kinematics_built_in(current_kin, &
1195 this%config%bodies(i), time_s)
1196
1197 ! User modifier (Superposition or Override)
1198 if (.not. associated(this%user_ale_rigid_kinematics, &
1200 call this%user_ale_rigid_kinematics(this%config%bodies(i)%id, &
1201 time_s, &
1202 current_kin%vel_trans, &
1203 current_kin%vel_ang)
1204 end if
1205
1206 current_kin%center = this%ale_pivot(i)%pos
1207 this%ale_pivot(i)%vel = current_kin%vel_trans
1208
1209 this%body_kin(i)%center = this%ale_pivot(i)%pos
1210 this%body_kin(i)%vel_trans = current_kin%vel_trans
1211 this%body_kin(i)%vel_ang = current_kin%vel_ang
1212
1213 ! Compute rotation matrix at current time
1214 call this%compute_rotation_matrix(i, time_s)
1215 rot_mat = this%body_rot_matrices(:,:,i)
1216 initial_rot_center = this%config%bodies(i)%rot_center
1217
1218 ! Accumulate contribution from each body and add to mesh velocity
1219 call add_kinematics_to_mesh_velocity(this%wm_x, this%wm_y, &
1220 this%wm_z, this%x_ref, this%y_ref, this%z_ref , &
1221 this%base_shapes(i), coef, current_kin, rot_mat, &
1222 initial_rot_center)
1223
1224 ! For checkpointing
1225 call this%prep_checkpoint(i)
1226 end do
1227
1228 ! If user has provided a custom function for mesh velocity.
1229 ! User mesh velocity will be added to the ale computed mesh velocity.
1230 ! This routine should not be used for rigid body motions!
1231 if (.not. associated(this%user_ale_mesh_vel, &
1233 call this%user_ale_mesh_vel(this%wm_x, this%wm_y, this%wm_z, &
1234 coef, this%x_ref, this%y_ref, this%z_ref, this%base_shapes, time_s)
1235 end if
1236
1237 call profiler_end_region('ALE add mesh velocity')
1238
1239 end subroutine update_mesh_velocity
1240
1242 subroutine advance_mesh(this, coef, time, nadv)
1243 class(ale_manager_t), intent(inout) :: this
1244 type(coef_t), intent(inout) :: coef
1245 type(time_state_t), intent(in) :: time
1246 integer, intent(in) :: nadv
1247 integer :: i
1248
1249 if (.not. this%active) return
1250 if (.not. this%has_moving_boundary) return
1251 call profiler_start_region('ALE update mesh')
1252 do i = 1, this%config%nbodies
1253 ! Advance Point Trackers attached to this body.
1254 ! Can be used for torque calculation (simcomp) at a point distanced
1255 ! from the body.
1256 ! or other purposes like tracking movement (user_check).
1257 call this%ghost_tracker_coord_step(this%body_kin(i), time, nadv, i)
1258 ! Update Pivot Location if requested
1259 call update_pivot_location(this%ale_pivot(i), &
1260 this%ale_pivot(i)%pos, &
1261 this%ale_pivot(i)%vel, &
1262 time, &
1263 nadv, &
1264 this%config%bodies(i))
1265 end do
1266
1267 ! Update lagged B terms (geometry history)
1268 call coef%update_B_history()
1269
1270 ! Update mesh coordinates
1271 call update_ale_mesh(coef, this%wm_x, this%wm_y, this%wm_z, &
1272 this%wm_x_lag, this%wm_y_lag, this%wm_z_lag, &
1273 time, nadv, "ab")
1274
1275 ! Update internal history of mesh velocity.
1276 call this%wm_x_lag%update()
1277 call this%wm_y_lag%update()
1278 call this%wm_z_lag%update()
1279 call profiler_end_region('ALE update mesh')
1280 end subroutine advance_mesh
1281
1282 ! Compute mesh stiffness with per-body gain/decay from stiff_geom.
1283 subroutine compute_stiffness_ale(coef, params)
1284 type(coef_t), intent(inout) :: coef
1285 type(ale_config_t), intent(in) :: params
1286 integer :: i, n, b, ierr
1287 integer, allocatable :: cheap_map(:)
1288 integer :: n_cheap, map_idx
1289 real(kind=rp) :: x, y, z
1290 real(kind=rp) :: raw_dist, body_stiff_val, max_added_stiff
1291 real(kind=rp) :: cx, cy, cz
1292 real(kind=rp) :: arg, decay, gain, norm_dist
1293 real(kind=rp) :: sample_start_time, sample_end_time, sample_time
1294 type(field_t), allocatable :: dist_fields(:)
1295 character(len=128) :: log_buf
1296
1297 n = coef%dof%size()
1298
1299 ! Check how many bodies need cheap_dist and create map
1300 allocate(cheap_map(params%nbodies))
1301 cheap_map = 0
1302 n_cheap = 0
1303
1304 do b = 1, params%nbodies
1305 if (trim(params%bodies(b)%stiff_geom%type) .eq. 'cheap_dist') then
1306 n_cheap = n_cheap + 1
1307 cheap_map(b) = n_cheap
1308 end if
1309 end do
1310
1311 ! Allocate and Compute cheap_dist only for required bodies
1312 if (n_cheap > 0) then
1313 allocate(dist_fields(n_cheap))
1314
1315 do b = 1, params%nbodies
1316 map_idx = cheap_map(b)
1317 if (map_idx .gt. 0) then
1318
1319 call dist_fields(map_idx)%init(coef%dof, "tmp_cheap_dist")
1320
1321 call neko_log%message(' ')
1322 call neko_log%message(" Start: cheap dist calculation " // &
1323 "for body '" // trim(params%bodies(b)%name) // "'")
1324
1325 call mpi_barrier(neko_comm, ierr)
1326 sample_start_time = mpi_wtime()
1327
1328 if (neko_bcknd_device .eq. 1) then
1329 call compute_cheap_dist_device(dist_fields(map_idx), coef, &
1330 coef%msh, params%bodies(b)%zone_indices, &
1331 copy_to_host = .true.)
1332 else
1333 call compute_cheap_dist_v2_cpu(dist_fields(map_idx), coef, &
1334 coef%msh, params%bodies(b)%zone_indices)
1335 end if
1336
1337 call mpi_barrier(neko_comm, ierr)
1338 sample_end_time = mpi_wtime()
1339 sample_time = sample_end_time - sample_start_time
1340
1341 write(log_buf, '(A, A, A, ES11.4, A)') " cheap dist for '", &
1342 trim(params%bodies(b)%name), "' took ", sample_time, " (s)"
1343 call neko_log%message(log_buf)
1344 end if
1345 end do
1346 end if
1347 call neko_log%message(' ')
1348
1349 ! Build stiffness field on Host
1350 select case (trim(params%stiffness_type))
1351 case ('built-in')
1352
1353 do concurrent(i = 1:n)
1354 x = coef%dof%x(i, 1, 1, 1)
1355 y = coef%dof%y(i, 1, 1, 1)
1356 z = coef%dof%z(i, 1, 1, 1)
1357
1358 max_added_stiff = 0.0_rp
1359
1360 ! Loop over bodies, calculate local contribution
1361 do b = 1, params%nbodies
1362 gain = params%bodies(b)%stiff_geom%gain
1363 if (trim(params%bodies(b)%stiff_geom%type) .eq. 'cheap_dist') then
1364 decay = params%bodies(b)%stiff_geom%stiff_dist
1365 else
1366 decay = params%bodies(b)%stiff_geom%radius
1367 end if
1368
1369 ! Geometry Center
1370 cx = params%bodies(b)%stiff_geom%center(1)
1371 cy = params%bodies(b)%stiff_geom%center(2)
1372 cz = params%bodies(b)%stiff_geom%center(3)
1373
1374 raw_dist = huge(0.0_rp)
1375
1376 ! Calculate Distance
1377 select case (trim(params%bodies(b)%stiff_geom%type))
1378 case ('sphere')
1379 raw_dist = sqrt((x - cx)**2 + (y - cy)**2 + (z - cz)**2)
1380
1381 case ('cylinder')
1382 ! Distance to Z-axis centered at (cx, cy)
1383 raw_dist = sqrt((x - cx)**2 + (y - cy)**2)
1384
1385 case ('box')
1386 ! ToDO
1387
1388 case ('cheap_dist')
1389 map_idx = cheap_map(b)
1390 if (map_idx .gt. 0) then
1391 raw_dist = dist_fields(map_idx)%x(i, 1, 1, 1)
1392 end if
1393 end select
1394
1395 ! Apply Profile
1396 body_stiff_val = 0.0_rp
1397 select case (trim(params%bodies(b)%stiff_geom%decay_profile))
1398 case ('gaussian')
1399 ! exp( -(r/decay)^2 )
1400 arg = -(raw_dist**2) / (decay**2)
1401 arg = arg * params%bodies(b)%stiff_geom%cutoff_coef
1402 body_stiff_val = gain * exp(arg)
1403
1404 case ('tanh')
1405 ! Tanh profile
1406 norm_dist = (raw_dist / decay)
1407 norm_dist = norm_dist * params%bodies(b)%stiff_geom%cutoff_coef
1408 body_stiff_val = gain * (1.0_rp - tanh(norm_dist))
1409 end select
1410
1411 if (body_stiff_val .gt. max_added_stiff) then
1412 max_added_stiff = body_stiff_val
1413 end if
1414 end do
1415
1416 coef%h1(i, 1, 1, 1) = 1.0_rp + max_added_stiff
1417 coef%h2(i, 1, 1, 1) = 0.0_rp
1418 end do
1419
1420 case default
1421 call neko_error("ALE Manager: Unknown stiffness type")
1422 end select
1423
1424 coef%ifh2 = .false.
1425
1426 if (neko_bcknd_device .eq. 1) then
1427 call device_memcpy(coef%h1, coef%h1_d, n, host_to_device, .false.)
1428 call device_memcpy(coef%h2, coef%h2_d, n, host_to_device, .true.)
1429 end if
1430
1431 if (allocated(dist_fields)) then
1432 do i = 1, size(dist_fields)
1433 call dist_fields(i)%free()
1434 end do
1435 deallocate(dist_fields)
1436 end if
1437 if (allocated(cheap_map)) deallocate(cheap_map)
1438
1439 end subroutine compute_stiffness_ale
1440
1441 ! Adds kinematics to mesh velocity.
1442 subroutine add_kinematics_to_mesh_velocity(wx, wy, wz, &
1443 x_ref, y_ref, z_ref, phi, coef, kinematics, rot_mat, initial_pivot_loc)
1444 type(field_t), intent(inout) :: wx, wy, wz
1445 type(field_t), intent(in) :: x_ref, y_ref, z_ref
1446 type(field_t), intent(in) :: phi
1447 type(coef_t), intent(in) :: coef
1448 type(body_kinematics_t), intent(in) :: kinematics
1449 real(kind=rp), intent(in) :: initial_pivot_loc(3)
1450 real(kind=rp), intent(in) :: rot_mat(3,3)
1451 if (neko_bcknd_device .eq. 1) then
1453 x_ref, y_ref, z_ref, &
1454 phi, coef, kinematics, rot_mat, initial_pivot_loc)
1455 else
1456 call add_kinematics_to_mesh_velocity_cpu(wx, wy, wz, &
1457 x_ref, y_ref, z_ref, &
1458 phi, coef, kinematics, rot_mat, initial_pivot_loc)
1459 end if
1460 end subroutine add_kinematics_to_mesh_velocity
1461
1462 ! Updates mesh position by integrating mesh velocity in time using AB scheme.
1463 subroutine update_ale_mesh(c_Xh, wm_x, wm_y, wm_z, wm_x_lag, wm_y_lag, &
1464 wm_z_lag, time, nadv, scheme_)
1465 type(coef_t), intent(inout) :: c_xh
1466 type(field_t), intent(in) :: wm_x, wm_y, wm_z
1467 type(field_series_t), intent(in) :: wm_x_lag, wm_y_lag, wm_z_lag
1468 type(time_state_t), intent(in) :: time
1469 integer, intent(in) :: nadv
1470 character(len=*), intent(in) :: scheme_
1471 if (neko_bcknd_device .eq. 1) then
1472 call update_ale_mesh_device(c_xh, wm_x, wm_y, wm_z, &
1473 wm_x_lag, wm_y_lag, wm_z_lag, time, nadv, scheme_)
1474 else
1475 call update_ale_mesh_cpu(c_xh, wm_x, wm_y, wm_z, &
1476 wm_x_lag, wm_y_lag, wm_z_lag, time, nadv, scheme_)
1477 end if
1478 end subroutine update_ale_mesh
1479
1480
1481 subroutine ale_manager_free(this)
1482 class(ale_manager_t), intent(inout), target :: this
1483 integer :: i
1484
1485 if (.not. this%active) return
1486
1487 call this%bc_moving%free()
1488 call this%bc_fixed%free()
1489 call this%bc_list%free()
1490
1491 if (allocated(this%base_shapes)) then
1492 do i = 1, size(this%base_shapes)
1493 call this%base_shapes(i)%free()
1494 end do
1495 deallocate(this%base_shapes)
1496 end if
1497
1498 call this%wm_x_lag%free()
1499 call this%wm_y_lag%free()
1500 call this%wm_z_lag%free()
1501 call this%x_ref%free()
1502 call this%y_ref%free()
1503 call this%z_ref%free()
1504
1505 if (allocated(this%ale_pivot)) deallocate(this%ale_pivot)
1506 if (allocated(this%config%bodies)) deallocate(this%config%bodies)
1507 if (allocated(this%body_kin)) deallocate(this%body_kin)
1508 if (associated(this%global_pivot_pos)) deallocate(this%global_pivot_pos)
1509 if (associated(this%global_pivot_vel_lag)) &
1510 deallocate(this%global_pivot_vel_lag)
1511 if (associated(this%global_basis_pos)) deallocate(this%global_basis_pos)
1512 if (associated(this%global_basis_vel_lag)) &
1513 deallocate(this%global_basis_vel_lag)
1514 if (allocated(this%ghost_handles)) deallocate(this%ghost_handles)
1515 if (allocated(this%body_rot_matrices)) deallocate(this%body_rot_matrices)
1516 if (allocated(this%trackers)) deallocate(this%trackers)
1517 if (associated(neko_ale, this)) nullify(neko_ale)
1518
1519 end subroutine ale_manager_free
1520
1522 subroutine ale_precon_factory(pc, ksp, coef, dof, gs, bclst, pctype, params)
1523 class(pc_t), allocatable, target, intent(inout) :: pc
1524 class(ksp_t), target, intent(inout) :: ksp
1525 type(coef_t), target, intent(in) :: coef
1526 type(dofmap_t), target, intent(in) :: dof
1527 type(gs_t), target, intent(inout) :: gs
1528 type(bc_list_t), target, intent(inout) :: bclst
1529 character(len=*), intent(in) :: pctype
1530 type(json_file), intent(inout) :: params
1531 call precon_factory(pc, pctype)
1532 select type (pcp => pc)
1533 type is (jacobi_t)
1534 call pcp%init(coef, dof, gs)
1535 type is (sx_jacobi_t)
1536 call pcp%init(coef, dof, gs)
1537 type is (device_jacobi_t)
1538 call pcp%init(coef, dof, gs)
1539 type is (hsmg_t)
1540 call pcp%init(coef, bclst, params)
1541 type is (phmg_t)
1542 call pcp%init(coef, bclst, params)
1543 end select
1544 call ksp%set_pc(pc)
1545 end subroutine ale_precon_factory
1546
1547 ! Sets the pivot state at restart.
1548 subroutine set_pivot_restart(this, time_restart)
1549 class(ale_manager_t), intent(inout) :: this
1550 real(kind=dp), intent(in) :: time_restart
1551 type(body_kinematics_t) :: kin_restart
1552 integer :: i, idx, handle_1, handle_2, offset_base
1553 type(time_state_t) :: time_state_dummy
1554 time_state_dummy%t = time_restart
1555
1556 !if (.not. allocated(this%global_pivot_pos)) return
1557
1558 do i = 1, this%config%nbodies
1559
1560 call compute_body_kinematics_built_in(kin_restart, &
1561 this%config%bodies(i), time_state_dummy)
1562
1563 ! User Modifier (Superposition or Override)
1564 if (.not. associated(this%user_ale_rigid_kinematics, &
1566 call this%user_ale_rigid_kinematics(this%config%bodies(i)%id, &
1567 time_state_dummy, &
1568 kin_restart%vel_trans, &
1569 kin_restart%vel_ang)
1570 end if
1571
1572 this%ale_pivot(i)%vel = kin_restart%vel_trans
1573
1574 idx = (i - 1) * 3
1575 ! Restore Position
1576 this%ale_pivot(i)%pos(1:3) = this%global_pivot_pos(idx + 1:idx + 3)
1577 this%body_kin(i)%center = this%ale_pivot(i)%pos
1578 this%body_kin(i)%vel_trans = kin_restart%vel_trans
1579 this%body_kin(i)%vel_ang = kin_restart%vel_ang
1580
1581 ! Restore Velocity History
1582 this%ale_pivot(i)%vel_lag(1:3, 1:3) = &
1583 this%global_pivot_vel_lag(idx + 1:idx + 3, :)
1584
1585
1586 offset_base = (i-1)*6
1587 handle_1 = this%ghost_handles(1, i)
1588 handle_2 = this%ghost_handles(2, i)
1589
1590 if ((handle_1 .gt. 0) .and. (handle_1 .le. this%n_trackers)) then
1591 this%trackers(handle_1)%pos = &
1592 this%global_basis_pos(offset_base + 1 : offset_base + 3)
1593
1594 ! Restore velocity history for ghost-x
1595 this%trackers(handle_1)%vel_lag = &
1596 this%global_basis_vel_lag(offset_base + 1 : offset_base + 3, :)
1597 end if
1598
1599 if ((handle_2 .gt. 0) .and. (handle_2 .le. this%n_trackers)) then
1600 this%trackers(handle_2)%pos = &
1601 this%global_basis_pos(offset_base + 4 : offset_base + 6)
1602
1603 ! Restore velocity history for ghost-y
1604 this%trackers(handle_2)%vel_lag = &
1605 this%global_basis_vel_lag(offset_base + 4 : offset_base + 6, :)
1606 end if
1607 end do
1608 end subroutine set_pivot_restart
1609
1610 ! Restores the current coef and related metrics
1611 ! and the pivot states at restart.
1612 subroutine sync_chkp(this, coef, Xh, adv, chkp, gs_Xh)
1613 class(ale_manager_t), intent(inout) :: this
1614 class(advection_t), intent(inout) :: adv
1615 type(coef_t), intent(inout) :: coef
1616 type(space_t), intent(inout) :: Xh
1617 type(chkp_t), intent(in) :: chkp
1618 type(gs_t), intent(inout) :: gs_Xh
1619 integer :: i, j, n
1620
1621 ! Return if ALE is not active.
1622 if (.not. this%active) return
1623
1624 if (allocated(chkp%previous_mesh%elements)) then
1625 call neko_error("ALE restart failed: " // &
1626 "The current mesh has a different number " // &
1627 "of elements than the checkpoint.")
1628 end if
1629
1630 ! Restarting from a different polynomial order
1631 if (chkp%previous_Xh%lx .ne. xh%lx) then
1632 n = coef%dof%size()
1633 associate(wm_x => this%wm_x, wm_y => this%wm_y, wm_z => this%wm_z)
1634 do concurrent(j = 1:n)
1635 ! Mesh Velocity
1636 wm_x%x(j,1,1,1) = wm_x%x(j,1,1,1) * coef%mult(j,1,1,1)
1637 wm_y%x(j,1,1,1) = wm_y%x(j,1,1,1) * coef%mult(j,1,1,1)
1638 wm_z%x(j,1,1,1) = wm_z%x(j,1,1,1) * coef%mult(j,1,1,1)
1639 end do
1640 end associate
1641
1642 do i = 1, this%wm_x_lag%size()
1643 do concurrent(j = 1:n)
1644 this%wm_x_lag%lf(i)%x(j,1,1,1) = &
1645 this%wm_x_lag%lf(i)%x(j,1,1,1) * coef%mult(j,1,1,1)
1646 this%wm_y_lag%lf(i)%x(j,1,1,1) = &
1647 this%wm_y_lag%lf(i)%x(j,1,1,1) * coef%mult(j,1,1,1)
1648 this%wm_z_lag%lf(i)%x(j,1,1,1) = &
1649 this%wm_z_lag%lf(i)%x(j,1,1,1) * coef%mult(j,1,1,1)
1650 end do
1651 end do
1652 end if
1653
1654 if (neko_bcknd_device .eq. 1) then
1655 call this%wm_x%copy_from(host_to_device, sync = .false.)
1656 call this%wm_y%copy_from(host_to_device, sync = .false.)
1657 call this%wm_z%copy_from(host_to_device, sync = .false.)
1658
1659 call this%wm_x_lag%lf(1)%copy_from(host_to_device, &
1660 sync = .false.)
1661 call this%wm_x_lag%lf(2)%copy_from(host_to_device, &
1662 sync = .false.)
1663
1664 call this%wm_y_lag%lf(1)%copy_from(host_to_device, &
1665 sync = .false.)
1666 call this%wm_y_lag%lf(2)%copy_from(host_to_device, &
1667 sync = .false.)
1668
1669 call this%wm_z_lag%lf(1)%copy_from(host_to_device, &
1670 sync = .false.)
1671 call this%wm_z_lag%lf(2)%copy_from(host_to_device, &
1672 sync = .false.)
1673
1674 if (c_associated(coef%dof%x_d)) then
1675 call device_memcpy(coef%dof%x, coef%dof%x_d, &
1676 size(coef%dof%x), host_to_device, sync = .false.)
1677 call device_memcpy(coef%dof%y, coef%dof%y_d, &
1678 size(coef%dof%y), host_to_device, sync = .false.)
1679 call device_memcpy(coef%dof%z, coef%dof%z_d, &
1680 size(coef%dof%z), host_to_device, sync = .false.)
1681 end if
1682
1683 if (c_associated(coef%Blag_d)) then
1684 call device_memcpy(coef%Blag, coef%Blag_d, size(coef%Blag), &
1685 host_to_device, sync = .false.)
1686 end if
1687
1688 if (c_associated(coef%Blaglag_d)) then
1689 call device_memcpy(coef%Blaglag, coef%Blaglag_d, &
1690 size(coef%Blaglag), host_to_device, sync = .false.)
1691 end if
1692 call device_sync()
1693 end if
1694
1695 ! Restarting from a different polynomial order
1696 if (chkp%previous_Xh%lx .ne. xh%lx) then
1697 call rotate_cyc(this%wm_x%x, this%wm_y%x, this%wm_z%x, 1, coef)
1698 call gs_xh%op(this%wm_x, gs_op_add)
1699 call gs_xh%op(this%wm_y, gs_op_add)
1700 call gs_xh%op(this%wm_z, gs_op_add)
1701 call rotate_cyc(this%wm_x%x, this%wm_y%x, this%wm_z%x, 0, coef)
1702
1703 do i = 1, this%wm_x_lag%size()
1704 call rotate_cyc(this%wm_x_lag%lf(i)%x, this%wm_y_lag%lf(i)%x, &
1705 this%wm_z_lag%lf(i)%x, 1, coef)
1706 call gs_xh%op(this%wm_x_lag%lf(i), gs_op_add)
1707 call gs_xh%op(this%wm_y_lag%lf(i), gs_op_add)
1708 call gs_xh%op(this%wm_z_lag%lf(i), gs_op_add)
1709 call rotate_cyc(this%wm_x_lag%lf(i)%x, this%wm_y_lag%lf(i)%x, &
1710 this%wm_z_lag%lf(i)%x, 0, coef)
1711 end do
1712 end if
1713
1714
1715 call this%set_pivot_restart(chkp%t)
1716 call coef%recompute_metrics()
1717
1718 ! If polynomial order changes during restart, we use current's mesh
1719 ! mass matrix for Blag and Blaglag. This will introduce some error,
1720 ! but maybe better than
1721 ! not restarting at all. Otherwise we need to save lagged mesh coordinates
1722 ! as well in order to be more accurate.
1723 if (chkp%previous_Xh%lx .ne. xh%lx) then
1724 coef%Blag = coef%B
1725 coef%Blaglag = coef%B
1726 if (neko_bcknd_device .eq. 1) then
1727 if (c_associated(coef%Blag_d)) then
1728 call device_memcpy(coef%Blag, coef%Blag_d, n, &
1729 host_to_device, sync = .false.)
1730 end if
1731 if (c_associated(coef%Blaglag_d)) then
1732 call device_memcpy(coef%Blaglag, coef%Blaglag_d, n, &
1733 host_to_device, sync = .false.)
1734 end if
1735 call device_sync()
1736 end if
1737 end if
1738
1739 call adv%recompute_metrics(coef, .true.)
1740 end subroutine sync_chkp
1741
1742 subroutine set_pivot_basis_for_checkpoint(this, body_idx)
1743 class(ale_manager_t), intent(inout) :: this
1744 integer, intent(in) :: body_idx
1745 integer :: idx, offset_base, h1, h2
1746
1747 if (.not. this%active) return
1748 if (.not. this%has_moving_boundary) return
1749
1750 idx = (body_idx - 1) * 3
1751 this%global_pivot_pos(idx + 1:idx + 3) = this%ale_pivot(body_idx)%pos(1:3)
1752 this%global_pivot_vel_lag(idx + 1:idx + 3, :) = &
1753 this%ale_pivot(body_idx)%vel_lag(1:3, 1:3)
1754
1755 h1 = this%ghost_handles(1, body_idx)
1756 h2 = this%ghost_handles(2, body_idx)
1757
1758 offset_base = (body_idx-1)*6
1759
1760 ! Save Positions
1761 this%global_basis_pos(offset_base + 1 : offset_base + 3) = &
1762 this%get_tracker_pos(h1)
1763 this%global_basis_pos(offset_base + 4 : offset_base + 6) = &
1764 this%get_tracker_pos(h2)
1765
1766 ! Ghost-x history
1767 this%global_basis_vel_lag(offset_base + 1 : offset_base + 3, :) = &
1768 this%trackers(h1)%vel_lag
1769
1770 ! Ghost-y history
1771 this%global_basis_vel_lag(offset_base + 4 : offset_base + 6, :) = &
1772 this%trackers(h2)%vel_lag
1773 end subroutine set_pivot_basis_for_checkpoint
1774
1775 ! Append val to arr if not already present.
1776 subroutine append_unique_int(arr, n, val)
1777 integer, allocatable, intent(inout) :: arr(:)
1778 integer, intent(inout) :: n
1779 integer, intent(in) :: val
1780 integer, allocatable :: tmp(:)
1781 integer :: k
1782
1783 do k = 1, n
1784 if (arr(k) .eq. val) return
1785 end do
1786
1787 allocate(tmp(n + 1))
1788 if (n .gt. 0) tmp(1:n) = arr(1:n)
1789 tmp(n + 1) = val
1790
1791 if (allocated(arr)) deallocate(arr)
1792 call move_alloc(tmp, arr)
1793
1794 n = n + 1
1795 end subroutine append_unique_int
1796
1798 subroutine mesh_preview(this, coef, json)
1799 class(ale_manager_t), intent(inout) :: this
1800 type(coef_t), intent(inout) :: coef
1801 type(json_file), intent(inout) :: json
1802 type(fld_file_output_t) :: fout
1803 type(field_t) :: dummy_field
1804 type(time_state_t) :: t_state
1805 type(file_t) :: out_file
1806 real(kind=rp) :: t_start
1807 real(kind=rp) :: t_end
1808 real(kind=rp) :: dt
1809 real(kind=rp) :: min_jac
1810 integer :: output_freq
1811 integer :: step, n_steps
1812 integer :: nadv, nadv_sim
1813 integer :: n
1814 logical :: mesh_preview_active
1815 character(len=128) :: log_buf
1816
1817 mesh_preview_active = .false.
1818
1819 if (json%valid_path('case.fluid.ale.mesh_preview.enabled')) then
1820 call json%get('case.fluid.ale.mesh_preview.enabled', &
1821 mesh_preview_active)
1822 end if
1823
1824 if (.not. mesh_preview_active) return
1825
1826 call json_get_or_default(json, 'case.fluid.ale.mesh_preview.start_time', &
1827 t_start, 0.0_rp)
1828 call json_get(json, 'case.fluid.ale.mesh_preview.end_time', &
1829 t_end)
1830 call json_get(json, 'case.fluid.ale.mesh_preview.dt', &
1831 dt)
1832 call json_get(json, &
1833 'case.fluid.ale.mesh_preview.output_freq', &
1834 output_freq)
1835
1836 call neko_log%section("ALE Mesh Preview")
1837 call neko_log%message("Executing mesh motion preview...")
1838
1839 n_steps = int((t_end - t_start) / dt)
1840 call json_get(json, 'case.numerics.time_order', nadv_sim)
1841
1842 write(log_buf, '(A, ES23.15)') ' Start Time : ', t_start
1843 call neko_log%message(log_buf)
1844 write(log_buf, '(A, ES23.15)') ' End Time : ', t_end
1845 call neko_log%message(log_buf)
1846 write(log_buf, '(A, ES23.15)') ' dt : ', dt
1847 call neko_log%message(log_buf)
1848 write(log_buf, '(A, I0)') ' Num Steps : ', n_steps
1849 call neko_log%message(log_buf)
1850 write(log_buf, '(A, I0)') ' Output Freq: ', output_freq
1851 call neko_log%message(log_buf)
1852 call neko_log%message('')
1853
1854 ! Setup dummy field for output
1855 call dummy_field%init(coef%dof, "mesh_preview")
1856 call field_rzero(dummy_field)
1857
1858 call fout%init(rp, "mesh_preview", 1)
1859 call fout%fields%assign_to_field(1, dummy_field)
1860 select type (ft => fout%file_%file_type)
1861 type is (fld_file_t)
1862 ft%write_mesh = .true.
1863 ft%skip_pressure = .false.
1864 end select
1865
1866
1867 ! Time Loop Setup
1868 step = 0
1869 nadv = 1
1870 t_state%t = t_start
1871 t_state%dt = dt
1872 t_state%tstep = 0
1873 t_state%dtlag = dt
1874 n = coef%dof%size()
1875
1876 if (neko_bcknd_device .eq. 1) then
1877 min_jac = device_glmin(coef%jac_d, n)
1878 else
1879 min_jac = glmin(coef%jac, n)
1880 end if
1881
1882 call sync_mesh_preview_step(coef, dummy_field)
1883 call fout%sample(t_state%t)
1884
1885 write(log_buf, '(A,I0, A,ES23.15, A,ES18.11)') &
1886 "Initial Mesh and Mass matrix saved! Step: ", step, " | Time:", &
1887 t_state%t, " | Min Jac: ", min_jac
1888
1889 call neko_log%message(trim(log_buf))
1890 call this%update_mesh_velocity(coef, t_state)
1891
1892 do step = 1, n_steps
1893 t_state%tstep = step
1894 t_state%t = t_start + (step * dt)
1895 nadv = min(step, nadv_sim)
1896
1897 call this%advance_mesh(coef, t_state, nadv)
1898 call coef%recompute_metrics()
1899
1900
1901 if (neko_bcknd_device .eq. 1) then
1902 min_jac = device_glmin(coef%jac_d, n)
1903 else
1904 min_jac = glmin(coef%jac, n)
1905 end if
1906
1907 if (min_jac .le. 0.0_rp) then
1908 write(log_buf, '(A, ES18.11, A, ES23.15)') &
1909 "Negative Jacobian detected (", min_jac, ") at t = ", &
1910 t_state%t
1911 call neko_log%message(log_buf)
1912
1913 call sync_mesh_preview_step(coef, dummy_field)
1914 call fout%sample(t_state%t)
1915
1916 write(log_buf, '(A,I0, A,ES23.15, A,ES18.11)') &
1917 "Mesh and Mass matrix saved! Step: ", step, " | Time:", &
1918 t_state%t, " | Min Jac:", min_jac
1919 call neko_log%message(trim(log_buf))
1920
1921 call neko_error("ALE Mesh Preview Aborted: Negative Jacobian found.")
1922 end if
1923
1924 if (mod(step, output_freq) .eq. 0) then
1925
1926 call sync_mesh_preview_step(coef, dummy_field)
1927 call fout%sample(t_state%t)
1928
1929 write(log_buf, '(A,I0, A,ES23.15, A,ES18.11)') &
1930 "Mesh and Mass matrix saved! Step: ", step, " | Time:", &
1931 t_state%t, " | Min Jac:", min_jac
1932 call neko_log%message(trim(log_buf))
1933
1934 end if
1935
1936 call this%update_mesh_velocity(coef, t_state)
1937
1938 end do
1939
1940 call dummy_field%free()
1941 call fout%free()
1942 call neko_log%end_section()
1943 call neko_log%message("Mesh preview complete.")
1944 call neko_error("ALE Mesh Preview Finished Successfully.")
1945
1946 end subroutine mesh_preview
1947
1948 subroutine sync_mesh_preview_step(coef, dummy_field)
1949 type(fld_file_output_t) :: fout
1950 type(coef_t), intent(inout) :: coef
1951 type(field_t), intent(inout) :: dummy_field
1952 integer :: n
1953
1954 n = coef%dof%size()
1955 if (neko_bcknd_device .eq. 1) then
1956 call device_copy(dummy_field%x_d, coef%B_d, n)
1957 else
1958 call copy(dummy_field%x, coef%B, n)
1959 end if
1960
1961 if (neko_bcknd_device .eq. 1) then
1962 associate(mesh => coef%dof)
1963 call device_memcpy(mesh%x, mesh%x_d, mesh%size(), &
1964 device_to_host, sync = .false.)
1965 call device_memcpy(mesh%y, mesh%y_d, mesh%size(), &
1966 device_to_host, sync = .false.)
1967 call device_memcpy(mesh%z, mesh%z_d, mesh%size(), &
1968 device_to_host, sync = .false.)
1969 end associate
1970 end if
1971
1972 end subroutine sync_mesh_preview_step
1973
1974 ! Asign a tracker point to a body. The tracker moves with body's
1975 ! rigid motion.
1976 function request_tracker(this, initial_pos, body_id) result(handle)
1977 class(ale_manager_t), intent(inout) :: this
1978 real(kind=rp), intent(in) :: initial_pos(3)
1979 integer, intent(in) :: body_id
1980 integer :: handle
1981 type(point_tracker_t), allocatable :: tmp(:)
1982
1983 handle = -100
1984 if (.not. this%active) return
1985 if (.not. this%has_moving_boundary) return
1986
1987 if (.not. allocated(this%trackers)) then
1988 allocate(this%trackers(30))
1989 this%n_trackers = 0
1990 elseif (this%n_trackers .ge. size(this%trackers)) then
1991 allocate(tmp(size(this%trackers) + 30))
1992 tmp(1:size(this%trackers)) = this%trackers
1993 deallocate(this%trackers)
1994 call move_alloc(tmp, this%trackers)
1995 end if
1996 this%n_trackers = this%n_trackers + 1
1997 handle = this%n_trackers
1998
1999 this%trackers(handle)%pos = initial_pos
2000 this%trackers(handle)%body_id = body_id
2001 this%trackers(handle)%vel_lag = this%ale_pivot(body_id)%vel_lag
2002 end function request_tracker
2003
2004 function get_tracker_pos(this, handle) result(pos)
2005 class(ale_manager_t), intent(in) :: this
2006 integer, intent(in) :: handle
2007 real(kind=rp) :: pos(3)
2008
2009 if (handle .gt. 0 .and. handle .le. this%n_trackers) then
2010 pos = this%trackers(handle)%pos
2011 else
2012 pos = 0.0_rp
2013 end if
2014 end function get_tracker_pos
2015
2016
2018 subroutine compute_rotation_matrix(this, body_idx, time)
2019 class(ale_manager_t), intent(inout) :: this
2020 integer, intent(in) :: body_idx
2021 type(time_state_t), intent(in) :: time
2022 integer :: h_x, h_y
2023 real(kind=rp) :: p(3), gx(3), gy(3)
2024 real(kind=rp) :: u(3), v(3), w(3), v_temp(3)
2025
2026 if (.not. this%active) return
2027 if (.not. this%has_moving_boundary) return
2028
2029 ! Get Points
2030 h_x = this%ghost_handles(1, body_idx)
2031 h_y = this%ghost_handles(2, body_idx)
2032
2033 p = this%ale_pivot(body_idx)%pos
2034 gx = this%get_tracker_pos(h_x)
2035 gy = this%get_tracker_pos(h_y)
2036
2037 ! Construct u (New X-axis)
2038 u = gx - p
2039 u = u / sqrt(sum(u**2))
2040
2041 ! Construct w via cross product (Z-axis)
2042 v_temp = gy - p
2043 w(1) = u(2)*v_temp(3) - u(3)*v_temp(2)
2044 w(2) = u(3)*v_temp(1) - u(1)*v_temp(3)
2045 w(3) = u(1)*v_temp(2) - u(2)*v_temp(1)
2046 w = w / sqrt(sum(w**2))
2047
2048 ! Construct v via orthogonalization (Y-axis)
2049 v(1) = w(2)*u(3) - w(3)*u(2)
2050 v(2) = w(3)*u(1) - w(1)*u(3)
2051 v(3) = w(1)*u(2) - w(2)*u(1)
2052
2053 this%body_rot_matrices(:, 1, body_idx) = u
2054 this%body_rot_matrices(:, 2, body_idx) = v
2055 this%body_rot_matrices(:, 3, body_idx) = w
2056
2057 end subroutine compute_rotation_matrix
2058
2059
2063 subroutine log_rot_angles(this, time, body_idxs)
2064 class(ale_manager_t), intent(in) :: this
2065 type(time_state_t), intent(in) :: time
2066 integer, optional, intent(in) :: body_idxs(:)
2067
2068 integer :: i, idx, n_log
2069 real(kind=rp) :: roll_deg, pitch_deg, yaw_deg
2070 real(kind=rp) :: r(3,3)
2071 character(len=256) :: log_buf
2072 real(kind=rp), parameter :: rad_to_deg = 180.0_rp / pi
2073
2074 if (.not. this%active) return
2075 if (.not. this%has_moving_boundary) return
2076
2077 if (present(body_idxs)) then
2078 n_log = size(body_idxs)
2079 else
2080 n_log = this%config%nbodies
2081 end if
2082
2083 call neko_log%message(" ")
2084 call neko_log%message("---------Rotation log---------")
2085 call neko_log%message("variable, time step, time, body, " // &
2086 "x_val, y_val, z_val")
2087
2088 ! If body_idxs is provided, only log those. Otherwise, log all.
2089 do i = 1, n_log
2090
2091 if (present(body_idxs)) then
2092 idx = body_idxs(i)
2093 else
2094 idx = i
2095 end if
2096
2097 r = this%body_rot_matrices(:,:,idx)
2098
2099 ! Angles
2100 yaw_deg = atan2(r(2,1), r(1,1)) * rad_to_deg
2101 pitch_deg = atan2(-r(3,1), sqrt(r(3,2)**2 + r(3,3)**2)) * rad_to_deg
2102 roll_deg = atan2(r(3,2), r(3,3)) * rad_to_deg
2103
2104 ! Log Rotation Angles (Roll, Pitch, Yaw) -> (X, Y, Z)
2105 write(log_buf, '(A, I0, A, ES13.6, A, A, A, 3(ES17.10, :, 2X))') &
2106 "Total_Rot_deg ", time%tstep, " ", time%t, " ", &
2107 trim(this%config%bodies(idx)%name), " ", &
2108 roll_deg, pitch_deg, yaw_deg
2109 call neko_log%message(trim(log_buf))
2110
2111 end do
2112
2113 end subroutine log_rot_angles
2114
2118 subroutine log_pivot(this, time, body_idxs)
2119 class(ale_manager_t), intent(in) :: this
2120 type(time_state_t), intent(in) :: time
2121 integer, optional, intent(in) :: body_idxs(:)
2122 integer :: i, idx, n_log
2123 real(kind=rp) :: pivot_pos(3), pivot_vel(3)
2124 character(len=256) :: log_buf
2125
2126 if (.not. this%active) return
2127 if (.not. this%has_moving_boundary) return
2128
2129 if (present(body_idxs)) then
2130 n_log = size(body_idxs)
2131 else
2132 n_log = this%config%nbodies
2133 end if
2134
2135 call neko_log%message(" ")
2136 call neko_log%message("----------Pivot Log-----------")
2137 call neko_log%message("variable, time step, time, body, " // &
2138 "x_val, y_val, z_val")
2139
2140 ! If body_idxs is provided, only log those. Otherwise, log all.
2141 do i = 1, n_log
2142
2143 if (present(body_idxs)) then
2144 idx = body_idxs(i)
2145 else
2146 idx = i
2147 end if
2148
2149 pivot_pos = this%ale_pivot(idx)%pos
2150 pivot_vel = this%ale_pivot(idx)%vel
2151
2152 ! Pivot Position
2153 write(log_buf, '(A, I0, A, ES13.6, A, A, A, 3(ES17.10, :, 2X))') &
2154 "Total_Pivot_pos ", time%tstep, " ", time%t, " ", &
2155 trim(this%config%bodies(idx)%name), " ", &
2156 this%ale_pivot(idx)%pos
2157 call neko_log%message(trim(log_buf))
2158
2159 ! Pivot Velocity
2160 write(log_buf, '(A, I0, A, ES13.6, A, A, A, 3(ES17.10, :, 2X))') &
2161 "Total_Pivot_vel ", time%tstep, " ", time%t, " ", &
2162 trim(this%config%bodies(idx)%name), " ", &
2163 this%ale_pivot(idx)%vel
2164 call neko_log%message(trim(log_buf))
2165 end do
2166
2167 end subroutine log_pivot
2168
2169 subroutine ghost_tracker_coord_step(this, kin_object, time_s, nadv, body_idx)
2170 class(ale_manager_t), intent(inout) :: this
2171 type(body_kinematics_t), intent(in) :: kin_object
2172 type(time_state_t), intent(in) :: time_s
2173 integer, intent(in) :: nadv
2174 integer, intent(in) :: body_idx
2175 integer :: t
2176 real(kind=rp) :: p_vel(3), rel_pos(3), v_tan(3)
2177
2178 if (.not. this%active) return
2179 if (.not. this%has_moving_boundary) return
2180
2181 if (allocated(this%trackers)) then
2182 do t = 1, this%n_trackers
2183 if (this%trackers(t)%body_id .eq. &
2184 this%config%bodies(body_idx)%id) then
2185 if (t .eq. this%ghost_handles(1, body_idx) .or. &
2186 t .eq. this%ghost_handles(2, body_idx)) then
2187
2188 ! Calculate the Arm vector (r) at current step
2189 rel_pos = this%trackers(t)%pos - kin_object%center
2190
2191 ! Calculate tangential velocity (Omega \cross r)
2192 v_tan(1) = kin_object%vel_ang(2) * rel_pos(3) - &
2193 kin_object%vel_ang(3) * rel_pos(2)
2194 v_tan(2) = kin_object%vel_ang(3) * rel_pos(1) - &
2195 kin_object%vel_ang(1) * rel_pos(3)
2196 v_tan(3) = kin_object%vel_ang(1) * rel_pos(2) - &
2197 kin_object%vel_ang(2) * rel_pos(1)
2198
2199 ! Total velocity
2200 p_vel = kin_object%vel_trans + v_tan
2201
2202 if (time_s%tstep .gt. 0) then
2203 call ab_integrate_point_pos(this%trackers(t)%pos, &
2204 this%trackers(t)%vel_lag, p_vel, time_s, nadv)
2205 end if
2206
2207 end if
2208
2209 end if
2210 end do
2211
2212 end if
2213 end subroutine ghost_tracker_coord_step
2214
2215 subroutine get_ale_solver_params_json(this, json, ksp_solver, precon_type, &
2216 precon_params, abstol, ksp_max_iter, res_monitor, import_base_shapes)
2217 class(ale_manager_t), intent(inout) :: this
2218 type(json_file), intent(inout) :: json
2219 character(len=:), allocatable, intent(inout) :: ksp_solver
2220 character(len=:), allocatable, intent(inout) :: precon_type
2221 type(json_file), intent(inout) :: precon_params
2222 real(kind=rp), intent(out) :: abstol
2223 integer, intent(out) :: ksp_max_iter
2224 logical, intent(out) :: res_monitor
2225 logical, intent(out) :: import_base_shapes
2226 logical :: tmp_logical
2227 character(len=:), allocatable :: tmp_str
2228
2229 if (allocated(ksp_solver)) deallocate(ksp_solver)
2230 if (allocated(precon_type)) deallocate(precon_type)
2231
2232 call json_get_or_default(json, &
2233 'case.fluid.ale.solver.import_base_shape', &
2234 import_base_shapes, .false.)
2235
2236 call json_get_or_default(json, 'case.fluid.ale.solver.type', &
2237 ksp_solver, 'cg')
2238
2239 call json_get_or_default(json, &
2240 'case.fluid.ale.solver.preconditioner.type', precon_type, 'jacobi')
2241
2242 if (json%valid_path('case.fluid.ale.solver.preconditioner')) then
2243 call json_get(json, 'case.fluid.ale.solver.preconditioner', &
2244 precon_params)
2245 end if
2246
2247 call json_get_or_default(json, &
2248 'case.fluid.ale.solver.absolute_tolerance', abstol, 1.0e-10_rp)
2249 call json_get_or_default(json, 'case.fluid.ale.solver.monitor', &
2250 res_monitor, .false.)
2251 call json_get_or_default(json, 'case.fluid.ale.solver.max_iterations', &
2252 ksp_max_iter, 10000)
2253
2254 if (json%valid_path('case.fluid.ale.solver.output_base_shape')) then
2255 call json%get('case.fluid.ale.solver.output_base_shape', tmp_logical)
2256 this%config%if_output_phi = tmp_logical
2257 end if
2258 if (json%valid_path('case.fluid.ale.solver.output_stiffness')) then
2259 call json%get('case.fluid.ale.solver.output_stiffness', tmp_logical)
2260 this%config%if_output_stiffness = tmp_logical
2261 end if
2262
2263 ! Mesh Stiffness
2264 if (json%valid_path('case.fluid.ale.solver.mesh_stiffness.type')) then
2265 call json%get('case.fluid.ale.solver.mesh_stiffness.type', tmp_str)
2266 this%config%stiffness_type = tmp_str
2267 if (.not. (trim(tmp_str) .eq. 'built-in')) then
2268 call neko_error("ALE: stiffness_type must be 'built-in'")
2269 end if
2270 end if
2271 end subroutine get_ale_solver_params_json
2272
2273 ! Register ALE fields for checkpointing.
2274 subroutine register_checkpoint_fields(this, coef, checkpoint)
2275 class(ale_manager_t), intent(inout) :: this
2276 type(coef_t), intent(inout) :: coef
2277 type(chkp_t), intent(inout) :: checkpoint
2278 integer :: i
2279
2280 if (.not. this%active) return
2281
2282 ! Add checkpoint data for ALE.
2283 call checkpoint%add_ale(coef%dof%x, coef%dof%y, &
2284 coef%dof%z, coef%dof%x_d, coef%dof%y_d, &
2285 coef%dof%z_d, &
2286 coef%Blag, coef%Blaglag, coef%Blag_d, coef%Blaglag_d, &
2287 this%wm_x, this%wm_y, this%wm_z, &
2288 this%wm_x_lag, this%wm_y_lag, &
2289 this%wm_z_lag, &
2290 this%global_pivot_pos, &
2291 this%global_pivot_vel_lag, &
2292 this%global_basis_pos, &
2293 this%global_basis_vel_lag)
2294
2295 end subroutine register_checkpoint_fields
2296end module ale_manager
Copy data between host and device (or device and device)
Definition device.F90:72
Synchronize a device or stream.
Definition device.F90:114
Retrieves a parameter by name or assigns a provided default value. In the latter case also adds the m...
Retrieves a parameter by name or throws an error.
Apply cyclic boundary condition to a vector field.
Abstract interface for user defined ALE base shapes.
Abstract interface for user defined ALE mesh velocity.
Abstract interface for user defined ALE rigid body kinematics.
Subroutines to add advection terms to the RHS of a transport equation.
Definition advection.f90:34
ALE Manager: Handles Mesh Motion.
type(ale_manager_t), pointer, public neko_ale
subroutine ale_manager_free(this)
subroutine, public log_pivot(this, time, body_idxs)
Logs pivot positions for all or selected bodies. can be called in usercompute. eg: call neko_alelog_p...
subroutine ale_precon_factory(pc, ksp, coef, dof, gs, bclst, pctype, params)
Factory for ALE Preconditioner.
real(kind=rp) function, dimension(3) get_tracker_pos(this, handle)
subroutine set_pivot_basis_for_checkpoint(this, body_idx)
subroutine compute_rotation_matrix(this, body_idx, time)
Computes Rotation Matrix.
subroutine, public update_ale_mesh(c_xh, wm_x, wm_y, wm_z, wm_x_lag, wm_y_lag, wm_z_lag, time, nadv, scheme_)
subroutine, public add_kinematics_to_mesh_velocity(wx, wy, wz, x_ref, y_ref, z_ref, phi, coef, kinematics, rot_mat, initial_pivot_loc)
subroutine update_mesh_velocity(this, coef, time_s)
Updates the mesh velocity field based on current time and kinematics Sums contributions from all bodi...
subroutine set_pivot_restart(this, time_restart)
subroutine, public compute_stiffness_ale(coef, params)
subroutine solve_base_mesh_displacement(this, coef, json, import_base_shapes, abstol, ksp_solver, ksp_max_iter, precon_type, precon_params, res_monitor)
Solves the Laplace equation to determine the base shape (phi) for each body. It finds a smooth blendi...
subroutine mesh_preview(this, coef, json)
Performs a preview of the mesh motion to verify quality/topology.
subroutine sync_mesh_preview_step(coef, dummy_field)
subroutine append_unique_int(arr, n, val)
subroutine get_ale_solver_params_json(this, json, ksp_solver, precon_type, precon_params, abstol, ksp_max_iter, res_monitor, import_base_shapes)
integer function request_tracker(this, initial_pos, body_id)
subroutine, public log_rot_angles(this, time, body_idxs)
Logs rotation angles for all or selected bodies. can be called in usercompute. eg: call neko_alelog_r...
subroutine register_checkpoint_fields(this, coef, checkpoint)
subroutine ale_manager_init(this, coef, json, user, chkp)
Initialize ALE Manager Sets up solver, registers fields, solves for base shape, etc.
subroutine ghost_tracker_coord_step(this, kin_object, time_s, nadv, body_idx)
subroutine advance_mesh(this, coef, time, nadv)
Main routine to advance the mesh in time.
subroutine sync_chkp(this, coef, xh, adv, chkp, gs_xh)
Defines data structures and algorithms for configuring, calculating, and time-integrating the rigid-b...
subroutine, public compute_body_kinematics_built_in(kinematics, body_conf, time)
Compute built-in kinematics for a body. Uses inputs from JSON. CPU-only.
subroutine, public ab_integrate_point_pos(pos, vel_lag, current_vel, time, nadv)
Advance a single point position (x,y,z) from the point's velocity using AB time-integration.
subroutine, public init_pivot_state(pivot, body_conf)
Initialize pivot state.
subroutine, public update_pivot_location(pivot, pivot_loc, pivot_vel, time, nadv, body_conf)
Updates pivot location.
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, 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)
subroutine, public add_kinematics_to_mesh_velocity_device(wx, wy, wz, x_ref, y_ref, z_ref, phi, coef, kinematics, rot_mat, inital_pivot_loc)
Add Kinematics to Mesh Velocity.
subroutine, public compute_cheap_dist_device(dist_field, coef, msh, zone_indices, copy_to_host)
Cheap dist device implementation.
subroutine, public update_ale_mesh_device(c_xh, wm_x, wm_y, wm_z, wm_x_lag, wm_y_lag, wm_z_lag, time, nadv, scheme_type)
Update ALE Mesh.
Defines a Matrix-vector product.
Definition ax.f90:34
Defines a list of bc_t.
Definition bc_list.f90:34
Defines a checkpoint.
Coefficients.
Definition coef.f90:34
Definition comm.F90:1
type(mpi_comm), public neko_comm
MPI communicator.
Definition comm.F90:45
Jacobi preconditioner accelerator backend.
subroutine, public device_copy(a_d, b_d, n, strm)
Copy a vector .
real(kind=rp) function, public device_glmin(a_d, n, strm)
Min of a vector of length n.
Device abstraction, common interface for various accelerators.
Definition device.F90:34
integer, parameter, public host_to_device
Definition device.F90:48
integer, parameter, public device_to_host
Definition device.F90:48
Defines a mapping of the degrees of freedom.
Definition dofmap.f90:35
subroutine, public field_rzero(a, n)
Zero a real vector.
subroutine, public field_add2(a, b, n)
Vector addition .
subroutine, public field_cmult(a, c, n)
Multiplication by constant c .
Contains the field_serties_t type.
Defines a field.
Definition field.f90:34
Module for file I/O operations.
Definition file.f90:34
Implements fld_file_output_t.
NEKTON fld file format.
Definition fld_file.f90:35
Gather-scatter.
Krylov preconditioner.
Definition pc_hsmg.f90:61
Importation of fields from fld files.
Jacobi preconditioner.
Definition pc_jacobi.f90:34
Utilities for retrieving parameters from the case files.
Implements the base abstract type for Krylov solvers plus helper types.
Definition krylov.f90:34
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
real(kind=rp), parameter, public pi
Definition math.f90:76
subroutine, public copy(a, b, n)
Copy a vector .
Definition math.f90:291
real(kind=rp) function, public glmin(a, n)
Min of a vector of length n.
Definition math.f90:688
Defines a mesh.
Definition mesh.f90:34
Build configurations.
integer, parameter neko_bcknd_hip
integer, parameter neko_bcknd_device
integer, parameter neko_bcknd_cuda
integer, parameter, public dp
Definition num_types.f90:9
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:12
Operators.
Definition operators.f90:34
Hybrid ph-multigrid preconditioner.
Definition phmg.f90:34
Krylov preconditioner.
Definition precon.f90:34
Profiling interface.
Definition profiler.F90:34
subroutine, public profiler_start_region(name, region_id)
Started a named (name) profiler region.
Definition profiler.F90:79
subroutine, public profiler_end_region(name, region_id)
End the most recently started profiler region.
Definition profiler.F90:116
Defines a registry for storing solution fields.
Definition registry.f90:34
type(registry_t), target, public neko_registry
Global field registry.
Definition registry.f90:144
Defines a function space.
Definition space.f90:34
Jacobi preconditioner SX-Aurora backend.
Module with things related to the simulation time.
Interfaces for user interaction with NEKO.
Definition user_intf.f90:34
subroutine, public dummy_user_ale_mesh_velocity(wm_x, wm_y, wm_z, coef, x_ref, y_ref, z_ref, base_shapes, time)
subroutine, public dummy_user_ale_base_shapes(base_shapes)
subroutine, public dummy_user_ale_rigid_kinematics(body_id, time, vel_trans, vel_ang)
Utilities.
Definition utils.f90:35
Defines a zero-valued Dirichlet boundary condition.
Base abstract type for computing the advection operator.
Definition advection.f90:46
Calculated Kinematics for a body at current time.
State history for time-integration of pivots.
Type for a tracked point linked to a body.
Base type for a matrix-vector product providing .
Definition ax.f90:43
A list of allocatable `bc_t`. Follows the standard interface of lists.
Definition bc_list.f90:49
Coefficients defined on a given (mesh, ) tuple. Arrays use indices (i,j,k,e): element e,...
Definition coef.f90:62
Defines a jacobi preconditioner.
Stores a series (sequence) of fields, logically connected to a base field, and arranged according to ...
A wrapper around a polymorphic generic_file_t that handles its init. This is essentially a factory fo...
Definition file.f90:56
Interface for NEKTON fld files.
Definition fld_file.f90:66
A simple output saving a list of fields to a .fld file.
Defines a jacobi preconditioner.
Definition pc_jacobi.f90:45
Type for storing initial and final residuals in a Krylov solver.
Definition krylov.f90:56
Base abstract type for a canonical Krylov method, solving .
Definition krylov.f90:73
Defines a canonical Krylov preconditioner.
Definition precon.f90:40
The function space for the SEM solution fields.
Definition space.f90:63
Defines a jacobi preconditioner for SX-Aurora.
A struct that contains all info about the time, expand as needed.
A type collecting all the overridable user routines and flag to suppress type injection from custom m...
Zero-valued Dirichlet boundary condition. Used for no-slip walls, but also for various auxillary cond...