Neko 1.1.0
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, 2)
821 call this%wm_y_lag%init(this%wm_y, 2)
822 call this%wm_z_lag%init(this%wm_z, 2)
823
824 if (allocated(moving_zone_ids)) deallocate(moving_zone_ids)
825 if (allocated(bc_type)) deallocate(bc_type)
826 if (allocated(zone_indices)) deallocate(zone_indices)
827 if (allocated(ksp_solver)) deallocate(ksp_solver)
828 if (allocated(precon_type)) deallocate(precon_type)
829 if (allocated(tmp_str)) deallocate(tmp_str)
830 if (allocated(tmp_vec)) deallocate(tmp_vec)
831
832 ! Performing mesh_preview.
833 call this%mesh_preview(coef, json)
834
835 ! Register checkpoint fields
836 call this%register_checkpoint_fields(coef, chkp)
837
838 call neko_log%end_section()
839 end subroutine ale_manager_init
840
845 subroutine solve_base_mesh_displacement(this, coef, json, &
846 import_base_shapes, abstol, ksp_solver, ksp_max_iter, precon_type, &
847 precon_params, res_monitor)
848 class(ale_manager_t), intent(inout), target :: this
849 class(ax_t), allocatable :: Ax
850 class(ksp_t), allocatable :: ksp
851 class(pc_t), allocatable :: pc
852 type(coef_t), intent(inout) :: coef
853 type(json_file), intent(inout) :: json
854 logical, intent(in) :: import_base_shapes
855 real(kind=rp), intent(in) :: abstol
856 logical, intent(in) :: res_monitor
857 character(len=*), intent(in) :: ksp_solver, precon_type
858 integer, intent(in) :: ksp_max_iter
859 type(json_file), intent(inout) :: precon_params
860 type(file_t) :: phi_file
861 type(field_t), pointer :: phi_ptr => null()
862 type(field_t) :: rhs_field
863 type(field_t) :: corr_field
864 type(ksp_monitor_t) :: monitor(1)
865 real(kind=rp) :: sample_start_time, sample_end_time
866 real(kind=rp) :: sample_time
867 character(len=LOG_SIZE) :: log_buf
868 integer :: n, i, m, k, ierr, body_idx, z_idx
869 integer :: j
870 real(kind=rp), allocatable :: h1_restore(:, :, :, :)
871 real(kind=rp), allocatable :: h2_restore(:, :, :, :)
872 type(zero_dirichlet_t) :: bc_active_body
873 type(zero_dirichlet_t) :: bc_inactive_body
874 type(bc_list_t) :: bcloc
875 type(bc_list_t) :: bcloc_zeros_only
876 type(json_file) :: body_sub
877 character(len=256) :: phi_fname
878 character(len=:), allocatable :: tmp_str
879
880
881 if (.not. this%active) return
882 if (.not. this%has_moving_boundary) return
883 if (this%config%nbodies .eq. 0) return
884
885 if (import_base_shapes) then
886 call neko_log%message(" ")
887 call neko_log%message("Importing ALE base shapes" // &
888 " (skipping Laplace solve)...")
889
890 do body_idx = 1, this%config%nbodies
891
892 call json_extract_item(json, 'case.fluid.ale.bodies', &
893 body_idx, body_sub)
894
895 call json_get(body_sub, 'base_shape_import_file', tmp_str)
896 phi_fname = tmp_str
897
898 phi_ptr => this%base_shapes(body_idx)
899
900 ! Load the field
901 call import_fields(fname = trim(phi_fname), p = phi_ptr)
902
903 call neko_log%message(" Loaded: " // &
904 trim(phi_fname) // &
905 " for body: " // &
906 trim(this%config%bodies(body_idx)%name))
907 end do
908
909 return
910 end if
911
912 call neko_log%message(" ")
913 call neko_log%message("Starting base mesh motion solve ...")
914 n = coef%dof%size()
915
916 call ax_helm_factory(ax, full_formulation = .false.)
917 call krylov_solver_factory(ksp, n, ksp_solver, &
918 ksp_max_iter, abstol, monitor = res_monitor)
919 call ale_precon_factory(pc, ksp, coef, coef%dof, &
920 coef%gs_h, this%bc_list, precon_type, precon_params)
921
922 ! Save original h1/h2
923 h1_restore = coef%h1
924 h2_restore = coef%h2
925
926 call rhs_field%init(coef%dof)
927 call corr_field%init(coef%dof)
928
929
930 ! User Defined Base Shapes (Skip Solver).
931 if (.not. associated(this%user_ale_base_shapes, &
933 call neko_log%message(" Using user-defined base shapes " // &
934 "(skipping Laplace solve)")
935
936 ! Call User Hook (Populates this%base_shapes)
937 call this%user_ale_base_shapes(this%base_shapes)
938
939 ! Compute phi_total (Sum of all user shapes)
940 if (this%config%nbodies .gt. 1) then
941 call field_rzero(this%phi_total)
942 do body_idx = 1, this%config%nbodies
943 call field_add2(this%phi_total, this%base_shapes(body_idx), n)
944 end do
945 end if
946
947 ! Output Shapes
948 if (this%config%if_output_phi) then
949 ! Individual Bodies
950 do body_idx = 1, this%config%nbodies
951 call phi_file%init('phi_' // &
952 trim(this%config%bodies(body_idx)%name) // '.fld', &
953 precision = rp)
954 select type (ft => phi_file%file_type)
955 type is (fld_file_t)
956 ft%skip_pressure = .false.
957 end select
958 call phi_file%write(this%base_shapes(body_idx))
959 call phi_file%free()
960 call neko_log%message(' phi_' // &
961 trim(this%config%bodies(body_idx)%name) // '.fld saved.')
962 end do
963
964 ! Total
965 if (this%config%nbodies .gt. 1) then
966 call neko_log%message(" phi_total.fld saved.")
967 select type (ft => phi_file%file_type)
968 type is (fld_file_t)
969 ft%skip_pressure = .false.
970 end select
971 call phi_file%init('phi_total.fld', precision = rp)
972 call phi_file%write(this%phi_total)
973 call phi_file%free()
974 end if
975 end if
976 else
977 ! Standard Laplace Solve (Requires Stiffness)
978
979 ! Compute Stiffness
980 call compute_stiffness_ale(coef, this%config)
981
982 ! Output Stiffness if requested (for diagnostic)
983 if (this%config%if_output_stiffness) then
984 rhs_field%x = coef%h1
985 call phi_file%init('stiffness.fld')
986 call phi_file%write(rhs_field)
987 call phi_file%free()
988 call field_rzero(rhs_field)
989 end if
990
991 ! Loop over bodies and Solve Laplace
992 do body_idx = 1, this%config%nbodies
993 call mpi_barrier(neko_comm, ierr)
994 sample_start_time = mpi_wtime()
995 call neko_log%message(" Solving laplace for body: " // &
996 trim(this%config%bodies(body_idx)%name))
997
998 call bc_active_body%init_from_components(coef)
999 call bc_inactive_body%init_from_components(coef)
1000
1001 ! Mark zones
1002 do j = 1, size(this%config%bodies(body_idx)%zone_indices)
1003 z_idx = this%config%bodies(body_idx)%zone_indices(j)
1004 call bc_active_body%mark_zone(coef%msh%labeled_zones(z_idx))
1005 end do
1006
1007 do i = 1, this%config%nbodies
1008 if (i /= body_idx) then
1009 do j = 1, size(this%config%bodies(i)%zone_indices)
1010 z_idx = this%config%bodies(i)%zone_indices(j)
1011 call bc_inactive_body%mark_zone(&
1012 coef%msh%labeled_zones(z_idx))
1013 end do
1014 end if
1015 end do
1016
1017 call bc_active_body%finalize()
1018 call bc_inactive_body%finalize()
1019
1020 ! The Full list for the solver (Freeze everything to 0 correction)
1021 call bcloc%init()
1022 call bcloc%append(this%bc_fixed)
1023 call bcloc%append(bc_active_body)
1024 call bcloc%append(bc_inactive_body)
1025
1026 ! The "Zeros Only" list for the field (Reset other boundaries)
1027 call bcloc_zeros_only%init()
1028 call bcloc_zeros_only%append(this%bc_fixed)
1029 call bcloc_zeros_only%append(bc_inactive_body)
1030
1031 call field_rzero(this%base_shapes(body_idx))
1032 this%base_shapes(body_idx)%x = 0.0_rp
1033 rhs_field%x = 0.0_rp
1034 corr_field%x = 0.0_rp
1035 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1036 ! phi = phi_corr + phi_lifted!
1037 ! A*phi_corr = -A*phi_lifted !
1038 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1039
1040 ! Lift BC (Dirichlet = 1.0 on moving body)
1041 m = bc_active_body%msk(0)
1042 do i = 1, m
1043 k = bc_active_body%msk(i)
1044 this%base_shapes(body_idx)%x(k, 1, 1, 1) = 1.0_rp
1045 end do
1046
1047 if (neko_bcknd_device .eq. 1) then
1048 call device_memcpy(this%base_shapes(body_idx)%x, &
1049 this%base_shapes(body_idx)%x_d, n, host_to_device, .true.)
1050 end if
1051
1052 ! Apply Zeros to others.
1053 ! This ensures fixed walls and other bodies are 0.0,
1054 ! even if they share grid with a moving wall.
1055 call bcloc_zeros_only%apply_scalar(this%base_shapes(body_idx)%x, n)
1056
1057 ! Compute RHS: RHS = -A * Phi_lifted.
1058 ! The following is motivated by implementation in Nek5000.
1059 call ax%compute(rhs_field%x, this%base_shapes(body_idx)%x, &
1060 coef, coef%msh, coef%Xh)
1061 call field_cmult(rhs_field, -1.0_rp)
1062
1063 ! Here we use the FULL list to apply zero Dirichlet BC
1064 ! on all boundaries.
1065 call bcloc%apply_scalar(rhs_field%x, n)
1066 call coef%gs_h%op(rhs_field, gs_op_add)
1067
1068 ! Solve
1069 call field_rzero(corr_field)
1070 call pc%update()
1071 monitor(1) = ksp%solve(ax, corr_field, &
1072 rhs_field%x, n, coef, bcloc, coef%gs_h)
1073
1074 ! phi = phi_lifted + phi_corr
1075 call field_add2(this%base_shapes(body_idx), corr_field, n)
1076
1077 ! Update Total Phi
1078 ! phi_total should be between 0 and 1.
1079 if (this%config%nbodies .gt. 1) then
1080 call field_add2(this%phi_total, this%base_shapes(body_idx), n)
1081 end if
1082
1083 call mpi_barrier(neko_comm, ierr)
1084 sample_end_time = mpi_wtime()
1085 sample_time = sample_end_time - sample_start_time
1086 write(log_buf, '(A, A, A, ES11.4, A)') " Laplace solve for '", &
1087 trim(this%config%bodies(body_idx)%name), "' took ", &
1088 sample_time, " (s)"
1089
1090 call neko_log%message(log_buf)
1091
1092 call bc_active_body%free()
1093 call bc_inactive_body%free()
1094 call bcloc%free()
1095 call bcloc_zeros_only%free()
1096
1097 ! We let the host to also have the base_shapes so in
1098 ! user_ale_mesh_vel
1099 ! it would be easier in general to use it.
1100 if (neko_bcknd_device .eq. 1) then
1101 call device_memcpy(this%base_shapes(body_idx)%x, &
1102 this%base_shapes(body_idx)%x_d, n, device_to_host, .true.)
1103 end if
1104
1105 if (this%config%if_output_phi) then
1106 call phi_file%init('phi_' // &
1107 trim(this%config%bodies(body_idx)%name) // '.fld', &
1108 precision = rp)
1109 select type (ft => phi_file%file_type)
1110 type is (fld_file_t)
1111 ft%skip_pressure = .false.
1112 end select
1113 call phi_file%write(this%base_shapes(body_idx))
1114 call phi_file%free()
1115 call neko_log%message(' phi_' // &
1116 trim(this%config%bodies(body_idx)%name) // '.fld saved.')
1117 end if
1118 end do
1119
1120 if (this%config%if_output_phi .and. (this%config%nbodies .gt. 1)) then
1121
1122 if (neko_bcknd_device .eq. 1) then
1123 call device_memcpy(this%phi_total%x, this%phi_total%x_d, n, &
1124 device_to_host, .true.)
1125 end if
1126
1127 call neko_log%message(" phi_total.fld saved.")
1128 call phi_file%init('phi_total.fld', precision = rp)
1129 select type (ft => phi_file%file_type)
1130 type is (fld_file_t)
1131 ft%skip_pressure = .false.
1132 end select
1133 call phi_file%write(this%phi_total)
1134 call phi_file%free()
1135 end if
1136 end if
1137
1138 call rhs_field%free()
1139 call corr_field%free()
1140 if (this%config%nbodies > 1) then
1141 call this%phi_total%free()
1142 end if
1143
1144 ! Restore h1/h2 to what they were before
1145 coef%h1(:,:,:,:) = h1_restore(:,:,:,:)
1146 coef%h2(:,:,:,:) = h2_restore(:,:,:,:)
1147 if (neko_bcknd_device .eq. 1) then
1148 call device_memcpy(coef%h1, coef%h1_d, n, host_to_device, .false.)
1149 call device_memcpy(coef%h2, coef%h2_d, n, host_to_device, .true.)
1150 end if
1151
1152 if (allocated(h1_restore)) deallocate(h1_restore)
1153 if (allocated(h2_restore)) deallocate(h2_restore)
1154 if (allocated(ax)) deallocate(ax)
1155 if (allocated(ksp)) then
1156 call ksp%free()
1157 deallocate(ksp)
1158 end if
1159 if (allocated(pc)) then
1160 call precon_destroy(pc)
1161 deallocate(pc)
1162 end if
1163
1164 end subroutine solve_base_mesh_displacement
1165
1168 subroutine update_mesh_velocity(this, coef, time_s)
1169 class(ale_manager_t), intent(inout) :: this
1170 type(coef_t), intent(in) :: coef
1171 type(time_state_t), intent(in) :: time_s
1172 integer :: i, n
1173 type(body_kinematics_t) :: current_kin
1174 real(kind=rp) :: rot_mat(3,3)
1175 real(kind=rp) :: initial_rot_center(3)
1176
1177 if (.not. this%active) return
1178 if (.not. this%has_moving_boundary) return
1179 call profiler_start_region('ALE add mesh velocity')
1180
1181 call field_rzero(this%wm_x)
1182 call field_rzero(this%wm_y)
1183 call field_rzero(this%wm_z)
1184
1185 do i = 1, this%config%nbodies
1186 ! Compute kinematics for built-in motions
1187 ! "current_kin" will be like solid body kinematics at current time
1188 call compute_body_kinematics_built_in(current_kin, &
1189 this%config%bodies(i), time_s)
1190
1191 ! User modifier (Superposition or Override)
1192 if (.not. associated(this%user_ale_rigid_kinematics, &
1194 call this%user_ale_rigid_kinematics(this%config%bodies(i)%id, &
1195 time_s, &
1196 current_kin%vel_trans, &
1197 current_kin%vel_ang)
1198 end if
1199
1200 current_kin%center = this%ale_pivot(i)%pos
1201 this%ale_pivot(i)%vel = current_kin%vel_trans
1202
1203 this%body_kin(i)%center = this%ale_pivot(i)%pos
1204 this%body_kin(i)%vel_trans = current_kin%vel_trans
1205 this%body_kin(i)%vel_ang = current_kin%vel_ang
1206
1207 ! Compute rotation matrix at current time
1208 call this%compute_rotation_matrix(i, time_s)
1209 rot_mat = this%body_rot_matrices(:,:,i)
1210 initial_rot_center = this%config%bodies(i)%rot_center
1211
1212 ! Accumulate contribution from each body and add to mesh velocity
1213 call add_kinematics_to_mesh_velocity(this%wm_x, this%wm_y, &
1214 this%wm_z, this%x_ref, this%y_ref, this%z_ref , &
1215 this%base_shapes(i), coef, current_kin, rot_mat, &
1216 initial_rot_center)
1217
1218 ! For checkpointing
1219 call this%prep_checkpoint(i)
1220 end do
1221
1222 ! If user has provided a custom function for mesh velocity.
1223 ! User mesh velocity will be added to the ale computed mesh velocity.
1224 ! This routine should not be used for rigid body motions!
1225 if (.not. associated(this%user_ale_mesh_vel, &
1227 call this%user_ale_mesh_vel(this%wm_x, this%wm_y, this%wm_z, &
1228 coef, this%x_ref, this%y_ref, this%z_ref, this%base_shapes, time_s)
1229 end if
1230
1231 call profiler_end_region('ALE add mesh velocity')
1232
1233 end subroutine update_mesh_velocity
1234
1236 subroutine advance_mesh(this, coef, time, nadv)
1237 class(ale_manager_t), intent(inout) :: this
1238 type(coef_t), intent(inout) :: coef
1239 type(time_state_t), intent(in) :: time
1240 integer, intent(in) :: nadv
1241 integer :: i
1242
1243 if (.not. this%active) return
1244 if (.not. this%has_moving_boundary) return
1245 call profiler_start_region('ALE update mesh')
1246 do i = 1, this%config%nbodies
1247 ! Advance Point Trackers attached to this body.
1248 ! Can be used for torque calculation (simcomp) at a point distanced
1249 ! from the body.
1250 ! or other purposes like tracking movement (user_check).
1251 call this%ghost_tracker_coord_step(this%body_kin(i), time, nadv, i)
1252 ! Update Pivot Location if requested
1253 call update_pivot_location(this%ale_pivot(i), &
1254 this%ale_pivot(i)%pos, &
1255 this%ale_pivot(i)%vel, &
1256 time, &
1257 nadv, &
1258 this%config%bodies(i))
1259 end do
1260
1261 ! Update lagged B terms (geometry history)
1262 call coef%update_B_history()
1263
1264 ! Update mesh coordinates
1265 call update_ale_mesh(coef, this%wm_x, this%wm_y, this%wm_z, &
1266 this%wm_x_lag, this%wm_y_lag, this%wm_z_lag, &
1267 time, nadv, "ab")
1268
1269 ! Update internal history of mesh velocity.
1270 call this%wm_x_lag%update()
1271 call this%wm_y_lag%update()
1272 call this%wm_z_lag%update()
1273 call profiler_end_region('ALE update mesh')
1274 end subroutine advance_mesh
1275
1276 ! Compute mesh stiffness with per-body gain/decay from stiff_geom.
1277 subroutine compute_stiffness_ale(coef, params)
1278 type(coef_t), intent(inout) :: coef
1279 type(ale_config_t), intent(in) :: params
1280 integer :: i, n, b, ierr
1281 integer, allocatable :: cheap_map(:)
1282 integer :: n_cheap, map_idx
1283 real(kind=rp) :: x, y, z
1284 real(kind=rp) :: raw_dist, body_stiff_val, max_added_stiff
1285 real(kind=rp) :: cx, cy, cz
1286 real(kind=rp) :: arg, decay, gain, norm_dist
1287 real(kind=rp) :: sample_start_time, sample_end_time, sample_time
1288 type(field_t), allocatable :: dist_fields(:)
1289 character(len=128) :: log_buf
1290
1291 n = coef%dof%size()
1292
1293 ! Check how many bodies need cheap_dist and create map
1294 allocate(cheap_map(params%nbodies))
1295 cheap_map = 0
1296 n_cheap = 0
1297
1298 do b = 1, params%nbodies
1299 if (trim(params%bodies(b)%stiff_geom%type) .eq. 'cheap_dist') then
1300 n_cheap = n_cheap + 1
1301 cheap_map(b) = n_cheap
1302 end if
1303 end do
1304
1305 ! Allocate and Compute cheap_dist only for required bodies
1306 if (n_cheap > 0) then
1307 allocate(dist_fields(n_cheap))
1308
1309 do b = 1, params%nbodies
1310 map_idx = cheap_map(b)
1311 if (map_idx .gt. 0) then
1312
1313 call dist_fields(map_idx)%init(coef%dof, "tmp_cheap_dist")
1314
1315 call neko_log%message(' ')
1316 call neko_log%message(" Start: cheap dist calculation " // &
1317 "for body '" // trim(params%bodies(b)%name) // "'")
1318
1319 call mpi_barrier(neko_comm, ierr)
1320 sample_start_time = mpi_wtime()
1321
1322 if (neko_bcknd_device .eq. 1) then
1323 call compute_cheap_dist_device(dist_fields(map_idx), coef, &
1324 coef%msh, params%bodies(b)%zone_indices, &
1325 copy_to_host = .true.)
1326 else
1327 call compute_cheap_dist_v2_cpu(dist_fields(map_idx), coef, &
1328 coef%msh, params%bodies(b)%zone_indices)
1329 end if
1330
1331 call mpi_barrier(neko_comm, ierr)
1332 sample_end_time = mpi_wtime()
1333 sample_time = sample_end_time - sample_start_time
1334
1335 write(log_buf, '(A, A, A, ES11.4, A)') " cheap dist for '", &
1336 trim(params%bodies(b)%name), "' took ", sample_time, " (s)"
1337 call neko_log%message(log_buf)
1338 end if
1339 end do
1340 end if
1341 call neko_log%message(' ')
1342
1343 ! Build stiffness field on Host
1344 select case (trim(params%stiffness_type))
1345 case ('built-in')
1346
1347 do concurrent(i = 1:n)
1348 x = coef%dof%x(i, 1, 1, 1)
1349 y = coef%dof%y(i, 1, 1, 1)
1350 z = coef%dof%z(i, 1, 1, 1)
1351
1352 max_added_stiff = 0.0_rp
1353
1354 ! Loop over bodies, calculate local contribution
1355 do b = 1, params%nbodies
1356 gain = params%bodies(b)%stiff_geom%gain
1357 if (trim(params%bodies(b)%stiff_geom%type) .eq. 'cheap_dist') then
1358 decay = params%bodies(b)%stiff_geom%stiff_dist
1359 else
1360 decay = params%bodies(b)%stiff_geom%radius
1361 end if
1362
1363 ! Geometry Center
1364 cx = params%bodies(b)%stiff_geom%center(1)
1365 cy = params%bodies(b)%stiff_geom%center(2)
1366 cz = params%bodies(b)%stiff_geom%center(3)
1367
1368 raw_dist = huge(0.0_rp)
1369
1370 ! Calculate Distance
1371 select case (trim(params%bodies(b)%stiff_geom%type))
1372 case ('sphere')
1373 raw_dist = sqrt((x - cx)**2 + (y - cy)**2 + (z - cz)**2)
1374
1375 case ('cylinder')
1376 ! Distance to Z-axis centered at (cx, cy)
1377 raw_dist = sqrt((x - cx)**2 + (y - cy)**2)
1378
1379 case ('box')
1380 ! ToDO
1381
1382 case ('cheap_dist')
1383 map_idx = cheap_map(b)
1384 if (map_idx .gt. 0) then
1385 raw_dist = dist_fields(map_idx)%x(i, 1, 1, 1)
1386 end if
1387 end select
1388
1389 ! Apply Profile
1390 body_stiff_val = 0.0_rp
1391 select case (trim(params%bodies(b)%stiff_geom%decay_profile))
1392 case ('gaussian')
1393 ! exp( -(r/decay)^2 )
1394 arg = -(raw_dist**2) / (decay**2)
1395 arg = arg * params%bodies(b)%stiff_geom%cutoff_coef
1396 body_stiff_val = gain * exp(arg)
1397
1398 case ('tanh')
1399 ! Tanh profile
1400 norm_dist = (raw_dist / decay)
1401 norm_dist = norm_dist * params%bodies(b)%stiff_geom%cutoff_coef
1402 body_stiff_val = gain * (1.0_rp - tanh(norm_dist))
1403 end select
1404
1405 if (body_stiff_val .gt. max_added_stiff) then
1406 max_added_stiff = body_stiff_val
1407 end if
1408 end do
1409
1410 coef%h1(i, 1, 1, 1) = 1.0_rp + max_added_stiff
1411 coef%h2(i, 1, 1, 1) = 0.0_rp
1412 end do
1413
1414 case default
1415 call neko_error("ALE Manager: Unknown stiffness type")
1416 end select
1417
1418 coef%ifh2 = .false.
1419
1420 if (neko_bcknd_device .eq. 1) then
1421 call device_memcpy(coef%h1, coef%h1_d, n, host_to_device, .false.)
1422 call device_memcpy(coef%h2, coef%h2_d, n, host_to_device, .true.)
1423 end if
1424
1425 if (allocated(dist_fields)) then
1426 do i = 1, size(dist_fields)
1427 call dist_fields(i)%free()
1428 end do
1429 deallocate(dist_fields)
1430 end if
1431 if (allocated(cheap_map)) deallocate(cheap_map)
1432
1433 end subroutine compute_stiffness_ale
1434
1435 ! Adds kinematics to mesh velocity.
1436 subroutine add_kinematics_to_mesh_velocity(wx, wy, wz, &
1437 x_ref, y_ref, z_ref, phi, coef, kinematics, rot_mat, initial_pivot_loc)
1438 type(field_t), intent(inout) :: wx, wy, wz
1439 type(field_t), intent(in) :: x_ref, y_ref, z_ref
1440 type(field_t), intent(in) :: phi
1441 type(coef_t), intent(in) :: coef
1442 type(body_kinematics_t), intent(in) :: kinematics
1443 real(kind=rp), intent(in) :: initial_pivot_loc(3)
1444 real(kind=rp), intent(in) :: rot_mat(3,3)
1445 if (neko_bcknd_device .eq. 1) then
1447 x_ref, y_ref, z_ref, &
1448 phi, coef, kinematics, rot_mat, initial_pivot_loc)
1449 else
1450 call add_kinematics_to_mesh_velocity_cpu(wx, wy, wz, &
1451 x_ref, y_ref, z_ref, &
1452 phi, coef, kinematics, rot_mat, initial_pivot_loc)
1453 end if
1454 end subroutine add_kinematics_to_mesh_velocity
1455
1456 ! Updates mesh position by integrating mesh velocity in time using AB scheme.
1457 subroutine update_ale_mesh(c_Xh, wm_x, wm_y, wm_z, wm_x_lag, wm_y_lag, &
1458 wm_z_lag, time, nadv, scheme_)
1459 type(coef_t), intent(inout) :: c_xh
1460 type(field_t), intent(in) :: wm_x, wm_y, wm_z
1461 type(field_series_t), intent(in) :: wm_x_lag, wm_y_lag, wm_z_lag
1462 type(time_state_t), intent(in) :: time
1463 integer, intent(in) :: nadv
1464 character(len=*), intent(in) :: scheme_
1465 if (neko_bcknd_device .eq. 1) then
1466 call update_ale_mesh_device(c_xh, wm_x, wm_y, wm_z, &
1467 wm_x_lag, wm_y_lag, wm_z_lag, time, nadv, scheme_)
1468 else
1469 call update_ale_mesh_cpu(c_xh, wm_x, wm_y, wm_z, &
1470 wm_x_lag, wm_y_lag, wm_z_lag, time, nadv, scheme_)
1471 end if
1472 end subroutine update_ale_mesh
1473
1474
1475 subroutine ale_manager_free(this)
1476 class(ale_manager_t), intent(inout), target :: this
1477 integer :: i
1478
1479 if (.not. this%active) return
1480
1481 call this%bc_moving%free()
1482 call this%bc_fixed%free()
1483 call this%bc_list%free()
1484
1485 if (allocated(this%base_shapes)) then
1486 do i = 1, size(this%base_shapes)
1487 call this%base_shapes(i)%free()
1488 end do
1489 deallocate(this%base_shapes)
1490 end if
1491
1492 call this%wm_x_lag%free()
1493 call this%wm_y_lag%free()
1494 call this%wm_z_lag%free()
1495 call this%x_ref%free()
1496 call this%y_ref%free()
1497 call this%z_ref%free()
1498
1499 if (allocated(this%ale_pivot)) deallocate(this%ale_pivot)
1500 if (allocated(this%config%bodies)) deallocate(this%config%bodies)
1501 if (allocated(this%body_kin)) deallocate(this%body_kin)
1502 if (associated(this%global_pivot_pos)) deallocate(this%global_pivot_pos)
1503 if (associated(this%global_pivot_vel_lag)) &
1504 deallocate(this%global_pivot_vel_lag)
1505 if (associated(this%global_basis_pos)) deallocate(this%global_basis_pos)
1506 if (associated(this%global_basis_vel_lag)) &
1507 deallocate(this%global_basis_vel_lag)
1508 if (allocated(this%ghost_handles)) deallocate(this%ghost_handles)
1509 if (allocated(this%body_rot_matrices)) deallocate(this%body_rot_matrices)
1510 if (allocated(this%trackers)) deallocate(this%trackers)
1511 if (associated(neko_ale, this)) nullify(neko_ale)
1512
1513 end subroutine ale_manager_free
1514
1516 subroutine ale_precon_factory(pc, ksp, coef, dof, gs, bclst, pctype, params)
1517 class(pc_t), allocatable, target, intent(inout) :: pc
1518 class(ksp_t), target, intent(inout) :: ksp
1519 type(coef_t), target, intent(in) :: coef
1520 type(dofmap_t), target, intent(in) :: dof
1521 type(gs_t), target, intent(inout) :: gs
1522 type(bc_list_t), target, intent(inout) :: bclst
1523 character(len=*), intent(in) :: pctype
1524 type(json_file), intent(inout) :: params
1525 call precon_factory(pc, pctype)
1526 select type (pcp => pc)
1527 type is (jacobi_t)
1528 call pcp%init(coef, dof, gs)
1529 type is (sx_jacobi_t)
1530 call pcp%init(coef, dof, gs)
1531 type is (device_jacobi_t)
1532 call pcp%init(coef, dof, gs)
1533 type is (hsmg_t)
1534 call pcp%init(coef, bclst, params)
1535 type is (phmg_t)
1536 call pcp%init(coef, bclst, params)
1537 end select
1538 call ksp%set_pc(pc)
1539 end subroutine ale_precon_factory
1540
1541 ! Sets the pivot state at restart.
1542 subroutine set_pivot_restart(this, time_restart)
1543 class(ale_manager_t), intent(inout) :: this
1544 real(kind=dp), intent(in) :: time_restart
1545 type(body_kinematics_t) :: kin_restart
1546 integer :: i, idx, handle_1, handle_2, offset_base
1547 type(time_state_t) :: time_state_dummy
1548 time_state_dummy%t = time_restart
1549
1550 !if (.not. allocated(this%global_pivot_pos)) return
1551
1552 do i = 1, this%config%nbodies
1553
1554 call compute_body_kinematics_built_in(kin_restart, &
1555 this%config%bodies(i), time_state_dummy)
1556
1557 ! User Modifier (Superposition or Override)
1558 if (.not. associated(this%user_ale_rigid_kinematics, &
1560 call this%user_ale_rigid_kinematics(this%config%bodies(i)%id, &
1561 time_state_dummy, &
1562 kin_restart%vel_trans, &
1563 kin_restart%vel_ang)
1564 end if
1565
1566 this%ale_pivot(i)%vel = kin_restart%vel_trans
1567
1568 idx = (i - 1) * 3
1569 ! Restore Position
1570 this%ale_pivot(i)%pos(1:3) = this%global_pivot_pos(idx + 1:idx + 3)
1571 this%body_kin(i)%center = this%ale_pivot(i)%pos
1572 this%body_kin(i)%vel_trans = kin_restart%vel_trans
1573 this%body_kin(i)%vel_ang = kin_restart%vel_ang
1574
1575 ! Restore Velocity History
1576 this%ale_pivot(i)%vel_lag(1:3, 1:3) = &
1577 this%global_pivot_vel_lag(idx + 1:idx + 3, :)
1578
1579
1580 offset_base = (i-1)*6
1581 handle_1 = this%ghost_handles(1, i)
1582 handle_2 = this%ghost_handles(2, i)
1583
1584 if ((handle_1 .gt. 0) .and. (handle_1 .le. this%n_trackers)) then
1585 this%trackers(handle_1)%pos = &
1586 this%global_basis_pos(offset_base + 1 : offset_base + 3)
1587
1588 ! Restore velocity history for ghost-x
1589 this%trackers(handle_1)%vel_lag = &
1590 this%global_basis_vel_lag(offset_base + 1 : offset_base + 3, :)
1591 end if
1592
1593 if ((handle_2 .gt. 0) .and. (handle_2 .le. this%n_trackers)) then
1594 this%trackers(handle_2)%pos = &
1595 this%global_basis_pos(offset_base + 4 : offset_base + 6)
1596
1597 ! Restore velocity history for ghost-y
1598 this%trackers(handle_2)%vel_lag = &
1599 this%global_basis_vel_lag(offset_base + 4 : offset_base + 6, :)
1600 end if
1601 end do
1602 end subroutine set_pivot_restart
1603
1604 ! Restores the current coef and related metrics
1605 ! and the pivot states at restart.
1606 subroutine sync_chkp(this, coef, Xh, adv, chkp, gs_Xh)
1607 class(ale_manager_t), intent(inout) :: this
1608 class(advection_t), intent(inout) :: adv
1609 type(coef_t), intent(inout) :: coef
1610 type(space_t), intent(inout) :: Xh
1611 type(chkp_t), intent(in) :: chkp
1612 type(gs_t), intent(inout) :: gs_Xh
1613 integer :: i, j, n
1614
1615 ! Return if ALE is not active.
1616 if (.not. this%active) return
1617
1618 if (allocated(chkp%previous_mesh%elements)) then
1619 call neko_error("ALE restart failed: " // &
1620 "The current mesh has a different number " // &
1621 "of elements than the checkpoint.")
1622 end if
1623
1624 ! Restarting from a different polynomial order
1625 if (chkp%previous_Xh%lx .ne. xh%lx) then
1626 n = coef%dof%size()
1627 associate(wm_x => this%wm_x, wm_y => this%wm_y, wm_z => this%wm_z)
1628 do concurrent(j = 1:n)
1629 ! Mesh Velocity
1630 wm_x%x(j,1,1,1) = wm_x%x(j,1,1,1) * coef%mult(j,1,1,1)
1631 wm_y%x(j,1,1,1) = wm_y%x(j,1,1,1) * coef%mult(j,1,1,1)
1632 wm_z%x(j,1,1,1) = wm_z%x(j,1,1,1) * coef%mult(j,1,1,1)
1633 end do
1634 end associate
1635
1636 do i = 1, this%wm_x_lag%size()
1637 do concurrent(j = 1:n)
1638 this%wm_x_lag%lf(i)%x(j,1,1,1) = &
1639 this%wm_x_lag%lf(i)%x(j,1,1,1) * coef%mult(j,1,1,1)
1640 this%wm_y_lag%lf(i)%x(j,1,1,1) = &
1641 this%wm_y_lag%lf(i)%x(j,1,1,1) * coef%mult(j,1,1,1)
1642 this%wm_z_lag%lf(i)%x(j,1,1,1) = &
1643 this%wm_z_lag%lf(i)%x(j,1,1,1) * coef%mult(j,1,1,1)
1644 end do
1645 end do
1646 end if
1647
1648 if (neko_bcknd_device .eq. 1) then
1649 call this%wm_x%copy_from(host_to_device, sync = .false.)
1650 call this%wm_y%copy_from(host_to_device, sync = .false.)
1651 call this%wm_z%copy_from(host_to_device, sync = .false.)
1652
1653 call this%wm_x_lag%lf(1)%copy_from(host_to_device, &
1654 sync = .false.)
1655 call this%wm_x_lag%lf(2)%copy_from(host_to_device, &
1656 sync = .false.)
1657
1658 call this%wm_y_lag%lf(1)%copy_from(host_to_device, &
1659 sync = .false.)
1660 call this%wm_y_lag%lf(2)%copy_from(host_to_device, &
1661 sync = .false.)
1662
1663 call this%wm_z_lag%lf(1)%copy_from(host_to_device, &
1664 sync = .false.)
1665 call this%wm_z_lag%lf(2)%copy_from(host_to_device, &
1666 sync = .false.)
1667
1668 if (c_associated(coef%dof%x_d)) then
1669 call device_memcpy(coef%dof%x, coef%dof%x_d, &
1670 size(coef%dof%x), host_to_device, sync = .false.)
1671 call device_memcpy(coef%dof%y, coef%dof%y_d, &
1672 size(coef%dof%y), host_to_device, sync = .false.)
1673 call device_memcpy(coef%dof%z, coef%dof%z_d, &
1674 size(coef%dof%z), host_to_device, sync = .false.)
1675 end if
1676
1677 if (c_associated(coef%Blag_d)) then
1678 call device_memcpy(coef%Blag, coef%Blag_d, size(coef%Blag), &
1679 host_to_device, sync = .false.)
1680 end if
1681
1682 if (c_associated(coef%Blaglag_d)) then
1683 call device_memcpy(coef%Blaglag, coef%Blaglag_d, &
1684 size(coef%Blaglag), host_to_device, sync = .false.)
1685 end if
1686 call device_sync()
1687 end if
1688
1689 ! Restarting from a different polynomial order
1690 if (chkp%previous_Xh%lx .ne. xh%lx) then
1691 call rotate_cyc(this%wm_x%x, this%wm_y%x, this%wm_z%x, 1, coef)
1692 call gs_xh%op(this%wm_x, gs_op_add)
1693 call gs_xh%op(this%wm_y, gs_op_add)
1694 call gs_xh%op(this%wm_z, gs_op_add)
1695 call rotate_cyc(this%wm_x%x, this%wm_y%x, this%wm_z%x, 0, coef)
1696
1697 do i = 1, this%wm_x_lag%size()
1698 call rotate_cyc(this%wm_x_lag%lf(i)%x, this%wm_y_lag%lf(i)%x, &
1699 this%wm_z_lag%lf(i)%x, 1, coef)
1700 call gs_xh%op(this%wm_x_lag%lf(i), gs_op_add)
1701 call gs_xh%op(this%wm_y_lag%lf(i), gs_op_add)
1702 call gs_xh%op(this%wm_z_lag%lf(i), gs_op_add)
1703 call rotate_cyc(this%wm_x_lag%lf(i)%x, this%wm_y_lag%lf(i)%x, &
1704 this%wm_z_lag%lf(i)%x, 0, coef)
1705 end do
1706 end if
1707
1708
1709 call this%set_pivot_restart(chkp%t)
1710 call coef%recompute_metrics()
1711
1712 ! If polynomial order changes during restart, we use current's mesh
1713 ! mass matrix for Blag and Blaglag. This will introduce some error,
1714 ! but maybe better than
1715 ! not restarting at all. Otherwise we need to save lagged mesh coordinates
1716 ! as well in order to be more accurate.
1717 if (chkp%previous_Xh%lx .ne. xh%lx) then
1718 coef%Blag = coef%B
1719 coef%Blaglag = coef%B
1720 if (neko_bcknd_device .eq. 1) then
1721 if (c_associated(coef%Blag_d)) then
1722 call device_memcpy(coef%Blag, coef%Blag_d, n, &
1723 host_to_device, sync = .false.)
1724 end if
1725 if (c_associated(coef%Blaglag_d)) then
1726 call device_memcpy(coef%Blaglag, coef%Blaglag_d, n, &
1727 host_to_device, sync = .false.)
1728 end if
1729 call device_sync()
1730 end if
1731 end if
1732
1733 call adv%recompute_metrics(coef, .true.)
1734 end subroutine sync_chkp
1735
1736 subroutine set_pivot_basis_for_checkpoint(this, body_idx)
1737 class(ale_manager_t), intent(inout) :: this
1738 integer, intent(in) :: body_idx
1739 integer :: idx, offset_base, h1, h2
1740
1741 if (.not. this%active) return
1742 if (.not. this%has_moving_boundary) return
1743
1744 idx = (body_idx - 1) * 3
1745 this%global_pivot_pos(idx + 1:idx + 3) = this%ale_pivot(body_idx)%pos(1:3)
1746 this%global_pivot_vel_lag(idx + 1:idx + 3, :) = &
1747 this%ale_pivot(body_idx)%vel_lag(1:3, 1:3)
1748
1749 h1 = this%ghost_handles(1, body_idx)
1750 h2 = this%ghost_handles(2, body_idx)
1751
1752 offset_base = (body_idx-1)*6
1753
1754 ! Save Positions
1755 this%global_basis_pos(offset_base + 1 : offset_base + 3) = &
1756 this%get_tracker_pos(h1)
1757 this%global_basis_pos(offset_base + 4 : offset_base + 6) = &
1758 this%get_tracker_pos(h2)
1759
1760 ! Ghost-x history
1761 this%global_basis_vel_lag(offset_base + 1 : offset_base + 3, :) = &
1762 this%trackers(h1)%vel_lag
1763
1764 ! Ghost-y history
1765 this%global_basis_vel_lag(offset_base + 4 : offset_base + 6, :) = &
1766 this%trackers(h2)%vel_lag
1767 end subroutine set_pivot_basis_for_checkpoint
1768
1769 ! Append val to arr if not already present.
1770 subroutine append_unique_int(arr, n, val)
1771 integer, allocatable, intent(inout) :: arr(:)
1772 integer, intent(inout) :: n
1773 integer, intent(in) :: val
1774 integer, allocatable :: tmp(:)
1775 integer :: k
1776
1777 do k = 1, n
1778 if (arr(k) .eq. val) return
1779 end do
1780
1781 allocate(tmp(n + 1))
1782 if (n .gt. 0) tmp(1:n) = arr(1:n)
1783 tmp(n + 1) = val
1784
1785 if (allocated(arr)) deallocate(arr)
1786 call move_alloc(tmp, arr)
1787
1788 n = n + 1
1789 end subroutine append_unique_int
1790
1792 subroutine mesh_preview(this, coef, json)
1793 class(ale_manager_t), intent(inout) :: this
1794 type(coef_t), intent(inout) :: coef
1795 type(json_file), intent(inout) :: json
1796 type(fld_file_output_t) :: fout
1797 type(field_t) :: dummy_field
1798 type(time_state_t) :: t_state
1799 type(file_t) :: out_file
1800 real(kind=rp) :: t_start
1801 real(kind=rp) :: t_end
1802 real(kind=rp) :: dt
1803 real(kind=rp) :: min_jac
1804 integer :: output_freq
1805 integer :: step, n_steps
1806 integer :: nadv, nadv_sim
1807 integer :: n
1808 logical :: mesh_preview_active
1809 character(len=128) :: log_buf
1810
1811 mesh_preview_active = .false.
1812
1813 if (json%valid_path('case.fluid.ale.mesh_preview.enabled')) then
1814 call json%get('case.fluid.ale.mesh_preview.enabled', &
1815 mesh_preview_active)
1816 end if
1817
1818 if (.not. mesh_preview_active) return
1819
1820 call json_get_or_default(json, 'case.fluid.ale.mesh_preview.start_time', &
1821 t_start, 0.0_rp)
1822 call json_get(json, 'case.fluid.ale.mesh_preview.end_time', &
1823 t_end)
1824 call json_get(json, 'case.fluid.ale.mesh_preview.dt', &
1825 dt)
1826 call json_get(json, &
1827 'case.fluid.ale.mesh_preview.output_freq', &
1828 output_freq)
1829
1830 call neko_log%section("ALE Mesh Preview")
1831 call neko_log%message("Executing mesh motion preview...")
1832
1833 n_steps = int((t_end - t_start) / dt)
1834 call json_get(json, 'case.numerics.time_order', nadv_sim)
1835
1836 write(log_buf, '(A, ES23.15)') ' Start Time : ', t_start
1837 call neko_log%message(log_buf)
1838 write(log_buf, '(A, ES23.15)') ' End Time : ', t_end
1839 call neko_log%message(log_buf)
1840 write(log_buf, '(A, ES23.15)') ' dt : ', dt
1841 call neko_log%message(log_buf)
1842 write(log_buf, '(A, I0)') ' Num Steps : ', n_steps
1843 call neko_log%message(log_buf)
1844 write(log_buf, '(A, I0)') ' Output Freq: ', output_freq
1845 call neko_log%message(log_buf)
1846 call neko_log%message('')
1847
1848 ! Setup dummy field for output
1849 call dummy_field%init(coef%dof, "mesh_preview")
1850 call field_rzero(dummy_field)
1851
1852 call fout%init(rp, "mesh_preview", 1)
1853 call fout%fields%assign_to_field(1, dummy_field)
1854 select type (ft => fout%file_%file_type)
1855 type is (fld_file_t)
1856 ft%write_mesh = .true.
1857 ft%skip_pressure = .false.
1858 end select
1859
1860
1861 ! Time Loop Setup
1862 step = 0
1863 nadv = 1
1864 t_state%t = t_start
1865 t_state%dt = dt
1866 t_state%tstep = 0
1867 t_state%dtlag = dt
1868 n = coef%dof%size()
1869
1870 if (neko_bcknd_device .eq. 1) then
1871 min_jac = device_glmin(coef%jac_d, n)
1872 else
1873 min_jac = glmin(coef%jac, n)
1874 end if
1875
1876 call sync_mesh_preview_step(coef, dummy_field)
1877 call fout%sample(t_state%t)
1878
1879 write(log_buf, '(A,I0, A,ES23.15, A,ES18.11)') &
1880 "Initial Mesh and Mass matrix saved! Step: ", step, " | Time:", &
1881 t_state%t, " | Min Jac: ", min_jac
1882
1883 call neko_log%message(trim(log_buf))
1884 call this%update_mesh_velocity(coef, t_state)
1885
1886 do step = 1, n_steps
1887 t_state%tstep = step
1888 t_state%t = t_start + (step * dt)
1889 nadv = min(step, nadv_sim)
1890
1891 call this%advance_mesh(coef, t_state, nadv)
1892 call coef%recompute_metrics()
1893
1894
1895 if (neko_bcknd_device .eq. 1) then
1896 min_jac = device_glmin(coef%jac_d, n)
1897 else
1898 min_jac = glmin(coef%jac, n)
1899 end if
1900
1901 if (min_jac .le. 0.0_rp) then
1902 write(log_buf, '(A, ES18.11, A, ES23.15)') &
1903 "Negative Jacobian detected (", min_jac, ") at t = ", &
1904 t_state%t
1905 call neko_log%message(log_buf)
1906
1907 call sync_mesh_preview_step(coef, dummy_field)
1908 call fout%sample(t_state%t)
1909
1910 write(log_buf, '(A,I0, A,ES23.15, A,ES18.11)') &
1911 "Mesh and Mass matrix saved! Step: ", step, " | Time:", &
1912 t_state%t, " | Min Jac:", min_jac
1913 call neko_log%message(trim(log_buf))
1914
1915 call neko_error("ALE Mesh Preview Aborted: Negative Jacobian found.")
1916 end if
1917
1918 if (mod(step, output_freq) .eq. 0) then
1919
1920 call sync_mesh_preview_step(coef, dummy_field)
1921 call fout%sample(t_state%t)
1922
1923 write(log_buf, '(A,I0, A,ES23.15, A,ES18.11)') &
1924 "Mesh and Mass matrix saved! Step: ", step, " | Time:", &
1925 t_state%t, " | Min Jac:", min_jac
1926 call neko_log%message(trim(log_buf))
1927
1928 end if
1929
1930 call this%update_mesh_velocity(coef, t_state)
1931
1932 end do
1933
1934 call dummy_field%free()
1935 call fout%free()
1936 call neko_log%end_section()
1937 call neko_log%message("Mesh preview complete.")
1938 call neko_error("ALE Mesh Preview Finished Successfully.")
1939
1940 end subroutine mesh_preview
1941
1942 subroutine sync_mesh_preview_step(coef, dummy_field)
1943 type(fld_file_output_t) :: fout
1944 type(coef_t), intent(inout) :: coef
1945 type(field_t), intent(inout) :: dummy_field
1946 integer :: n
1947
1948 n = coef%dof%size()
1949 if (neko_bcknd_device .eq. 1) then
1950 call device_copy(dummy_field%x_d, coef%B_d, n)
1951 else
1952 call copy(dummy_field%x, coef%B, n)
1953 end if
1954
1955 if (neko_bcknd_device .eq. 1) then
1956 associate(mesh => coef%dof)
1957 call device_memcpy(mesh%x, mesh%x_d, mesh%size(), &
1958 device_to_host, sync = .false.)
1959 call device_memcpy(mesh%y, mesh%y_d, mesh%size(), &
1960 device_to_host, sync = .false.)
1961 call device_memcpy(mesh%z, mesh%z_d, mesh%size(), &
1962 device_to_host, sync = .false.)
1963 end associate
1964 end if
1965
1966 end subroutine sync_mesh_preview_step
1967
1968 ! Asign a tracker point to a body. The tracker moves with body's
1969 ! rigid motion.
1970 function request_tracker(this, initial_pos, body_id) result(handle)
1971 class(ale_manager_t), intent(inout) :: this
1972 real(kind=rp), intent(in) :: initial_pos(3)
1973 integer, intent(in) :: body_id
1974 integer :: handle
1975 type(point_tracker_t), allocatable :: tmp(:)
1976
1977 handle = -100
1978 if (.not. this%active) return
1979 if (.not. this%has_moving_boundary) return
1980
1981 if (.not. allocated(this%trackers)) then
1982 allocate(this%trackers(30))
1983 this%n_trackers = 0
1984 elseif (this%n_trackers .ge. size(this%trackers)) then
1985 allocate(tmp(size(this%trackers) + 30))
1986 tmp(1:size(this%trackers)) = this%trackers
1987 deallocate(this%trackers)
1988 call move_alloc(tmp, this%trackers)
1989 end if
1990 this%n_trackers = this%n_trackers + 1
1991 handle = this%n_trackers
1992
1993 this%trackers(handle)%pos = initial_pos
1994 this%trackers(handle)%body_id = body_id
1995 this%trackers(handle)%vel_lag = this%ale_pivot(body_id)%vel_lag
1996 end function request_tracker
1997
1998 function get_tracker_pos(this, handle) result(pos)
1999 class(ale_manager_t), intent(in) :: this
2000 integer, intent(in) :: handle
2001 real(kind=rp) :: pos(3)
2002
2003 if (handle .gt. 0 .and. handle .le. this%n_trackers) then
2004 pos = this%trackers(handle)%pos
2005 else
2006 pos = 0.0_rp
2007 end if
2008 end function get_tracker_pos
2009
2010
2012 subroutine compute_rotation_matrix(this, body_idx, time)
2013 class(ale_manager_t), intent(inout) :: this
2014 integer, intent(in) :: body_idx
2015 type(time_state_t), intent(in) :: time
2016 integer :: h_x, h_y
2017 real(kind=rp) :: p(3), gx(3), gy(3)
2018 real(kind=rp) :: u(3), v(3), w(3), v_temp(3)
2019
2020 if (.not. this%active) return
2021 if (.not. this%has_moving_boundary) return
2022
2023 ! Get Points
2024 h_x = this%ghost_handles(1, body_idx)
2025 h_y = this%ghost_handles(2, body_idx)
2026
2027 p = this%ale_pivot(body_idx)%pos
2028 gx = this%get_tracker_pos(h_x)
2029 gy = this%get_tracker_pos(h_y)
2030
2031 ! Construct u (New X-axis)
2032 u = gx - p
2033 u = u / sqrt(sum(u**2))
2034
2035 ! Construct w via cross product (Z-axis)
2036 v_temp = gy - p
2037 w(1) = u(2)*v_temp(3) - u(3)*v_temp(2)
2038 w(2) = u(3)*v_temp(1) - u(1)*v_temp(3)
2039 w(3) = u(1)*v_temp(2) - u(2)*v_temp(1)
2040 w = w / sqrt(sum(w**2))
2041
2042 ! Construct v via orthogonalization (Y-axis)
2043 v(1) = w(2)*u(3) - w(3)*u(2)
2044 v(2) = w(3)*u(1) - w(1)*u(3)
2045 v(3) = w(1)*u(2) - w(2)*u(1)
2046
2047 this%body_rot_matrices(:, 1, body_idx) = u
2048 this%body_rot_matrices(:, 2, body_idx) = v
2049 this%body_rot_matrices(:, 3, body_idx) = w
2050
2051 end subroutine compute_rotation_matrix
2052
2053
2057 subroutine log_rot_angles(this, time, body_idxs)
2058 class(ale_manager_t), intent(in) :: this
2059 type(time_state_t), intent(in) :: time
2060 integer, optional, intent(in) :: body_idxs(:)
2061
2062 integer :: i, idx, n_log
2063 real(kind=rp) :: roll_deg, pitch_deg, yaw_deg
2064 real(kind=rp) :: r(3,3)
2065 character(len=256) :: log_buf
2066 real(kind=rp), parameter :: rad_to_deg = 180.0_rp / pi
2067
2068 if (.not. this%active) return
2069 if (.not. this%has_moving_boundary) return
2070
2071 if (present(body_idxs)) then
2072 n_log = size(body_idxs)
2073 else
2074 n_log = this%config%nbodies
2075 end if
2076
2077 call neko_log%message(" ")
2078 call neko_log%message("---------Rotation log---------")
2079 call neko_log%message("variable, time step, time, body, " // &
2080 "x_val, y_val, z_val")
2081
2082 ! If body_idxs is provided, only log those. Otherwise, log all.
2083 do i = 1, n_log
2084
2085 if (present(body_idxs)) then
2086 idx = body_idxs(i)
2087 else
2088 idx = i
2089 end if
2090
2091 r = this%body_rot_matrices(:,:,idx)
2092
2093 ! Angles
2094 yaw_deg = atan2(r(2,1), r(1,1)) * rad_to_deg
2095 pitch_deg = atan2(-r(3,1), sqrt(r(3,2)**2 + r(3,3)**2)) * rad_to_deg
2096 roll_deg = atan2(r(3,2), r(3,3)) * rad_to_deg
2097
2098 ! Log Rotation Angles (Roll, Pitch, Yaw) -> (X, Y, Z)
2099 write(log_buf, '(A, I0, A, ES13.6, A, A, A, 3(ES17.10, :, 2X))') &
2100 "Total_Rot_deg ", time%tstep, " ", time%t, " ", &
2101 trim(this%config%bodies(idx)%name), " ", &
2102 roll_deg, pitch_deg, yaw_deg
2103 call neko_log%message(trim(log_buf))
2104
2105 end do
2106
2107 end subroutine log_rot_angles
2108
2112 subroutine log_pivot(this, time, body_idxs)
2113 class(ale_manager_t), intent(in) :: this
2114 type(time_state_t), intent(in) :: time
2115 integer, optional, intent(in) :: body_idxs(:)
2116 integer :: i, idx, n_log
2117 real(kind=rp) :: pivot_pos(3), pivot_vel(3)
2118 character(len=256) :: log_buf
2119
2120 if (.not. this%active) return
2121 if (.not. this%has_moving_boundary) return
2122
2123 if (present(body_idxs)) then
2124 n_log = size(body_idxs)
2125 else
2126 n_log = this%config%nbodies
2127 end if
2128
2129 call neko_log%message(" ")
2130 call neko_log%message("----------Pivot Log-----------")
2131 call neko_log%message("variable, time step, time, body, " // &
2132 "x_val, y_val, z_val")
2133
2134 ! If body_idxs is provided, only log those. Otherwise, log all.
2135 do i = 1, n_log
2136
2137 if (present(body_idxs)) then
2138 idx = body_idxs(i)
2139 else
2140 idx = i
2141 end if
2142
2143 pivot_pos = this%ale_pivot(idx)%pos
2144 pivot_vel = this%ale_pivot(idx)%vel
2145
2146 ! Pivot Position
2147 write(log_buf, '(A, I0, A, ES13.6, A, A, A, 3(ES17.10, :, 2X))') &
2148 "Total_Pivot_pos ", time%tstep, " ", time%t, " ", &
2149 trim(this%config%bodies(idx)%name), " ", &
2150 this%ale_pivot(idx)%pos
2151 call neko_log%message(trim(log_buf))
2152
2153 ! Pivot Velocity
2154 write(log_buf, '(A, I0, A, ES13.6, A, A, A, 3(ES17.10, :, 2X))') &
2155 "Total_Pivot_vel ", time%tstep, " ", time%t, " ", &
2156 trim(this%config%bodies(idx)%name), " ", &
2157 this%ale_pivot(idx)%vel
2158 call neko_log%message(trim(log_buf))
2159 end do
2160
2161 end subroutine log_pivot
2162
2163 subroutine ghost_tracker_coord_step(this, kin_object, time_s, nadv, body_idx)
2164 class(ale_manager_t), intent(inout) :: this
2165 type(body_kinematics_t), intent(in) :: kin_object
2166 type(time_state_t), intent(in) :: time_s
2167 integer, intent(in) :: nadv
2168 integer, intent(in) :: body_idx
2169 integer :: t
2170 real(kind=rp) :: p_vel(3), rel_pos(3), v_tan(3)
2171
2172 if (.not. this%active) return
2173 if (.not. this%has_moving_boundary) return
2174
2175 if (allocated(this%trackers)) then
2176 do t = 1, this%n_trackers
2177 if (this%trackers(t)%body_id .eq. &
2178 this%config%bodies(body_idx)%id) then
2179 if (t .eq. this%ghost_handles(1, body_idx) .or. &
2180 t .eq. this%ghost_handles(2, body_idx)) then
2181
2182 ! Calculate the Arm vector (r) at current step
2183 rel_pos = this%trackers(t)%pos - kin_object%center
2184
2185 ! Calculate tangential velocity (Omega \cross r)
2186 v_tan(1) = kin_object%vel_ang(2) * rel_pos(3) - &
2187 kin_object%vel_ang(3) * rel_pos(2)
2188 v_tan(2) = kin_object%vel_ang(3) * rel_pos(1) - &
2189 kin_object%vel_ang(1) * rel_pos(3)
2190 v_tan(3) = kin_object%vel_ang(1) * rel_pos(2) - &
2191 kin_object%vel_ang(2) * rel_pos(1)
2192
2193 ! Total velocity
2194 p_vel = kin_object%vel_trans + v_tan
2195
2196 if (time_s%tstep .gt. 0) then
2197 call ab_integrate_point_pos(this%trackers(t)%pos, &
2198 this%trackers(t)%vel_lag, p_vel, time_s, nadv)
2199 end if
2200
2201 end if
2202
2203 end if
2204 end do
2205
2206 end if
2207 end subroutine ghost_tracker_coord_step
2208
2209 subroutine get_ale_solver_params_json(this, json, ksp_solver, precon_type, &
2210 precon_params, abstol, ksp_max_iter, res_monitor, import_base_shapes)
2211 class(ale_manager_t), intent(inout) :: this
2212 type(json_file), intent(inout) :: json
2213 character(len=:), allocatable, intent(inout) :: ksp_solver
2214 character(len=:), allocatable, intent(inout) :: precon_type
2215 type(json_file), intent(inout) :: precon_params
2216 real(kind=rp), intent(out) :: abstol
2217 integer, intent(out) :: ksp_max_iter
2218 logical, intent(out) :: res_monitor
2219 logical, intent(out) :: import_base_shapes
2220 logical :: tmp_logical
2221 character(len=:), allocatable :: tmp_str
2222
2223 if (allocated(ksp_solver)) deallocate(ksp_solver)
2224 if (allocated(precon_type)) deallocate(precon_type)
2225
2226 call json_get_or_default(json, &
2227 'case.fluid.ale.solver.import_base_shape', &
2228 import_base_shapes, .false.)
2229
2230 call json_get_or_default(json, 'case.fluid.ale.solver.type', &
2231 ksp_solver, 'cg')
2232
2233 call json_get_or_default(json, &
2234 'case.fluid.ale.solver.preconditioner.type', precon_type, 'jacobi')
2235
2236 if (json%valid_path('case.fluid.ale.solver.preconditioner')) then
2237 call json_get(json, 'case.fluid.ale.solver.preconditioner', &
2238 precon_params)
2239 end if
2240
2241 call json_get_or_default(json, &
2242 'case.fluid.ale.solver.absolute_tolerance', abstol, 1.0e-10_rp)
2243 call json_get_or_default(json, 'case.fluid.ale.solver.monitor', &
2244 res_monitor, .false.)
2245 call json_get_or_default(json, 'case.fluid.ale.solver.max_iterations', &
2246 ksp_max_iter, 10000)
2247
2248 if (json%valid_path('case.fluid.ale.solver.output_base_shape')) then
2249 call json%get('case.fluid.ale.solver.output_base_shape', tmp_logical)
2250 this%config%if_output_phi = tmp_logical
2251 end if
2252 if (json%valid_path('case.fluid.ale.solver.output_stiffness')) then
2253 call json%get('case.fluid.ale.solver.output_stiffness', tmp_logical)
2254 this%config%if_output_stiffness = tmp_logical
2255 end if
2256
2257 ! Mesh Stiffness
2258 if (json%valid_path('case.fluid.ale.solver.mesh_stiffness.type')) then
2259 call json%get('case.fluid.ale.solver.mesh_stiffness.type', tmp_str)
2260 this%config%stiffness_type = tmp_str
2261 if (.not. (trim(tmp_str) .eq. 'built-in')) then
2262 call neko_error("ALE: stiffness_type must be 'built-in'")
2263 end if
2264 end if
2265 end subroutine get_ale_solver_params_json
2266
2267 ! Register ALE fields for checkpointing.
2268 subroutine register_checkpoint_fields(this, coef, checkpoint)
2269 class(ale_manager_t), intent(inout), target :: this
2270 type(coef_t), intent(inout) :: coef
2271 type(chkp_t), intent(inout) :: checkpoint
2272 integer :: i
2273
2274 if (.not. this%active) return
2275
2276 ! Add checkpoint data for ALE.
2277 call checkpoint%add_ale(coef%dof%x, coef%dof%y, &
2278 coef%dof%z, coef%dof%x_d, coef%dof%y_d, &
2279 coef%dof%z_d, &
2280 coef%Blag, coef%Blaglag, coef%Blag_d, coef%Blaglag_d, &
2281 this%wm_x, this%wm_y, this%wm_z, &
2282 this%wm_x_lag, this%wm_y_lag, &
2283 this%wm_z_lag, &
2284 this%global_pivot_pos, &
2285 this%global_pivot_vel_lag, &
2286 this%global_basis_pos, &
2287 this%global_basis_vel_lag)
2288
2289 end subroutine register_checkpoint_fields
2290end 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:119
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:46
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:63
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.
Gather-scatter kernel.
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:64
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...