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