Neko 1.99.7
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_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 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 ! force all elements as deformed when mesh changes.
218 call coef%msh%all_deformed()
219
220 if (neko_bcknd_device .eq. 1) then
221 if ((.not. (neko_bcknd_hip .eq. 1)) .and. &
222 (.not. (neko_bcknd_cuda .eq. 1))) then
223 call neko_error("ALE currently " // &
224 "supported only with HIP or CUDA backend.")
225 end if
226 end if
227 if (oifs) then
228 call neko_error("ALE not currently supported with OIFS.")
229 end if
230 if (json%valid_path('case.checkpoint_format')) then
231 call json_get(json, 'case.checkpoint_format', tmp_str)
232 if (trim(tmp_str) /= 'chkp') then
233 call neko_error("ALE is not supported with the '" // &
234 trim(tmp_str) // &
235 "' checkpoint format. Please use 'chkp'.")
236 end if
237 end if
238 neko_ale => this
239 end if
240
241 call neko_log%section("ALE Initialization")
242 call neko_log%message(" ")
243
244 if (neko_bcknd_hip .eq. 1) then
245 call neko_log%message("Initializing ALE " // &
246 "with device backend (HIP).")
247 else if (neko_bcknd_cuda .eq. 1) then
248 call neko_log%message("Initializing ALE " // &
249 "with device backend (CUDA).")
250 else
251 call neko_log%message("Initializing ALE " // &
252 "with CPU backend.")
253 end if
254
255 tmp_logical = .false.
256 n = coef%dof%size()
257
258 call this%x_ref%init(coef%dof, "x_ref")
259 call this%y_ref%init(coef%dof, "y_ref")
260 call this%z_ref%init(coef%dof, "z_ref")
261
262 call copy(this%x_ref%x, coef%dof%x, n)
263 call copy(this%y_ref%x, coef%dof%y, n)
264 call copy(this%z_ref%x, coef%dof%z, n)
265
266 ! Sync to device
267 if (neko_bcknd_device .eq. 1) then
268 call this%x_ref%copy_from(host_to_device, .false.)
269 call this%y_ref%copy_from(host_to_device, .false.)
270 call this%z_ref%copy_from(host_to_device, .true.)
271 end if
272
273 ! Set user function pointers.
274 this%user_ale_mesh_vel => user%ale_mesh_velocity
275 this%user_ale_base_shapes => user%ale_base_shapes
276 this%user_ale_rigid_kinematics => user%ale_rigid_kinematics
277
278 ! Check user association states
279 has_user_rigid_kin = .not. associated(this%user_ale_rigid_kinematics, &
281 has_user_mesh_vel = .not. associated(this%user_ale_mesh_vel, &
283
284 ! Enable B history (Blag, Blaglag)
285 call coef%enable_B_history()
286 call json_get(json, 'case.numerics.time_order', time_order)
287
288 ! Stuff for zone_id checks
289 n_moving_zones = 0
290 if (allocated(moving_zone_ids)) deallocate(moving_zone_ids)
291 allocate(moving_zone_ids(0))
292
293 ! Register mesh velocity fields
294 call neko_registry%add_field(coef%dof, 'wm_x')
295 call neko_registry%add_field(coef%dof, 'wm_y')
296 call neko_registry%add_field(coef%dof, 'wm_z')
297 this%wm_x => neko_registry%get_field('wm_x')
298 this%wm_y => neko_registry%get_field('wm_y')
299 this%wm_z => neko_registry%get_field('wm_z')
300
301 call get_ale_solver_params_json(this, json, ksp_solver, precon_type, &
302 precon_params, abstol, ksp_max_iter, res_monitor, import_base_shapes)
303
304 ! Mark BCs
305 call this%bc_moving%init_from_components(coef)
306 call this%bc_fixed%init_from_components(coef)
307
308 if (json%valid_path('case.fluid.boundary_conditions')) then
309 call json%info('case.fluid.boundary_conditions', n_children = n_bcs)
310
311 do i = 1, n_bcs
312 call json_extract_item(json, 'case.fluid.boundary_conditions', &
313 i, bc_subdict)
314
315 if (allocated(bc_type)) deallocate(bc_type)
316 call json_get(bc_subdict, 'type', bc_type)
317
318 if (allocated(zone_indices)) deallocate(zone_indices)
319 call json_get(bc_subdict, 'zone_indices', zone_indices)
320
321 moving_ = .false.
322 if (trim(bc_type) .eq. 'no_slip') then
323 call json_get_or_default(bc_subdict, 'moving', moving_, .false.)
324 end if
325
326 if (moving_) then
327 do j = 1, size(zone_indices)
328 ! we append unique moving zone ids for future checks
329 call append_unique_int(moving_zone_ids, n_moving_zones, &
330 zone_indices(j))
331 call this%bc_moving%mark_zone(coef%msh%labeled_zones(&
332 zone_indices(j)))
333 end do
334 this%has_moving_boundary = .true.
335 else
336 do j = 1, size(zone_indices)
337 call this%bc_fixed%mark_zone(coef%msh%labeled_zones(&
338 zone_indices(j)))
339 end do
340 end if
341 end do
342 end if
343
344 call this%bc_moving%finalize()
345 call this%bc_fixed%finalize()
346 call this%bc_list%init()
347 call this%bc_list%append(this%bc_moving)
348 call this%bc_list%append(this%bc_fixed)
349
350 ! Mesh Stiffness
351 if (json%valid_path('case.fluid.ale.solver.mesh_stiffness.type')) then
352 call json%get('case.fluid.ale.solver.mesh_stiffness.type', tmp_str)
353 this%config%stiffness_type = tmp_str
354 if (.not. (trim(tmp_str) .eq. 'built-in')) then
355 call neko_error("ALE: stiffness_type must be 'built-in'")
356 end if
357 end if
358
359 if ( associated(this%user_ale_base_shapes, &
360 dummy_user_ale_base_shapes) .and. (.not. import_base_shapes)) then
361 call neko_log%message('Solver Type : (' // &
362 trim(ksp_solver) // ', ' // trim(precon_type) // ')')
363 write(log_buf, '(A,ES13.6)') 'Abs tol :', abstol
364 call neko_log%message(log_buf)
365 call neko_log%message('Mesh Stiffness : ' // &
366 trim(this%config%stiffness_type))
367 end if
368 call neko_log%message(' ')
369
370 ! Bodies
371 if (json%valid_path('case.fluid.ale.bodies')) then
372 call json%info('case.fluid.ale.bodies', n_children = n_bodies)
373 this%config%nbodies = n_bodies
374 allocate(this%config%bodies(n_bodies))
375 allocate(this%ale_pivot(n_bodies))
376 allocate(this%body_kin(n_bodies))
377 allocate(this%base_shapes(n_bodies))
378 allocate(this%global_pivot_pos(3 * this%config%nbodies))
379 allocate(this%global_pivot_vel_lag(3 * this%config%nbodies, 3))
380 allocate(this%global_basis_pos(6 * this%config%nbodies))
381 allocate(this%ghost_handles(2, this%config%nbodies))
382 allocate(this%global_basis_vel_lag(6 * this%config%nbodies, 3))
383 allocate(this%body_rot_matrices(3, 3, this%config%nbodies))
384
385 this%global_pivot_pos = 0.0_rp
386 this%global_pivot_vel_lag = 0.0_rp
387 this%global_basis_pos = 0.0_rp
388 this%global_basis_vel_lag = 0.0_rp
389 this%body_rot_matrices = 0.0_rp
390
391 do i = 1, n_bodies
392 this%body_rot_matrices(1, 1, i) = 1.0_rp
393 this%body_rot_matrices(2, 2, i) = 1.0_rp
394 this%body_rot_matrices(3, 3, i) = 1.0_rp
395 end do
396
397 do i = 1, n_bodies
398 call json_extract_item(json, 'case.fluid.ale.bodies', i, body_sub)
399 this%config%bodies(i)%id = i
400
401 if (body_sub%valid_path('name')) then
402 call json_get(body_sub, 'name', tmp_str)
403 this%config%bodies(i)%name = tmp_str
404 else
405 write(this%config%bodies(i)%name, '(A,I0)') 'body_', i
406 endif
407
408 if (body_sub%valid_path('zone_indices')) then
409 call json_get(body_sub, 'zone_indices', zone_indices)
410 this%config%bodies(i)%zone_indices = zone_indices
411 else
412 call neko_error("ALE: body " // &
413 trim(this%config%bodies(i)%name) // &
414 " must have 'zone_indices'")
415 endif
416
417 ! Oscillation
418 this%config%bodies(i)%osc_amp = 0.0_rp
419 this%config%bodies(i)%osc_freq = 0.0_rp
420 if (body_sub%valid_path('oscillation')) then
421 call json_get(body_sub, 'oscillation.amplitude', tmp_vec, &
422 expected_size = 3)
423 this%config%bodies(i)%osc_amp = tmp_vec
424 call json_get(body_sub, 'oscillation.frequency', tmp_vec, &
425 expected_size = 3)
426 this%config%bodies(i)%osc_freq = tmp_vec
427 end if
428
429 ! Rotation
430 if (body_sub%valid_path('rotation')) then
431 ! Check if pivot exists.
432 if (.not. body_sub%valid_path('pivot')) then
433 call neko_error("ale.bodies.pivot is missing " // &
434 "from the case file.")
435 end if
436
437 call json_get(body_sub, 'rotation.type', tmp_str)
438 this%config%bodies(i)%rotation_type = tmp_str
439
440 select case (trim(tmp_str))
441 case ('harmonic')
442 call json_get(body_sub, 'rotation.amplitude_deg', tmp_vec, &
443 expected_size = 3)
444 this%config%bodies(i)%rot_amp_degree = tmp_vec
445
446 call json_get(body_sub, 'rotation.frequency', tmp_vec, &
447 expected_size = 3)
448 this%config%bodies(i)%rot_freq = tmp_vec
449
450
451 case ('ramp')
452 call json_get(body_sub, 'rotation.ramp_t0', tmp_vec, &
453 expected_size = 3)
454 this%config%bodies(i)%ramp_t0 = tmp_vec
455
456 call json_get(body_sub, 'rotation.ramp_omega0', tmp_vec, &
457 expected_size = 3)
458 this%config%bodies(i)%ramp_omega0 = tmp_vec
459
460
461 case ('smooth_step')
462 call json_get_or_default(body_sub, 'rotation.axis', &
463 tmp_int, 3)
464 if (tmp_int .ge. 1 .and. tmp_int .le. 3) then
465 this%config%bodies(i)%rotation_axis = tmp_int
466 else
467 call neko_error("ALE: rotation.axis must be (integer) " // &
468 "1 -> x, 2 -> y, or 3 -> z")
469 end if
470 call json_get(body_sub, 'rotation.step_control_times', &
471 tmp_vec, expected_size = 4)
472 this%config%bodies(i)%step_control_times = tmp_vec
473
474 call json_get(body_sub, 'rotation.target_angle_deg', tmp_val)
475 this%config%bodies(i)%target_rot_angle_deg = tmp_val
476
477 case default
478 call neko_error("ALE: rotation.type must be 'harmonic', " // &
479 "'ramp', or 'smooth_step'")
480 end select
481 end if
482
483 ! Rotation Center
484 if (body_sub%valid_path('pivot')) then
485 call json_get_or_default(body_sub, 'pivot.type', tmp_str, &
486 'relative')
487 this%config%bodies(i)%rotation_center_type = tmp_str
488 call json_get(body_sub, 'pivot.value', tmp_vec, expected_size = 3)
489 this%config%bodies(i)%rot_center = tmp_vec
490
491
492 tmp_str = this%config%bodies(i)%rotation_center_type
493 if (trim(tmp_str) /= 'relative' .and. &
494 trim(tmp_str) /= 'relative_sin') then
495 call neko_error("ALE: pivot.type must be " // &
496 "'relative', or 'relative_sin'.")
497 end if
498 end if
499
500 ! Stiff Geom
501 if (body_sub%valid_path('stiff_geom')) then
502 call json_get(body_sub, 'stiff_geom.type', tmp_str)
503 this%config%bodies(i)%stiff_geom%type = tmp_str
504 call json_get(body_sub, 'stiff_geom.gain', &
505 this%config%bodies(i)%stiff_geom%gain)
506 call json_get(body_sub, 'stiff_geom.decay_profile', tmp_str)
507 this%config%bodies(i)%stiff_geom%decay_profile = tmp_str
508
509 select case (trim(this%config%bodies(i)%stiff_geom%decay_profile))
510 case ('gaussian')
511 call json_get_or_default(body_sub, &
512 'stiff_geom.cutoff_coef', &
513 this%config%bodies(i)%stiff_geom%cutoff_coef, 9.0_rp)
514 case ('tanh')
515 call json_get_or_default(body_sub, &
516 'stiff_geom.cutoff_coef', &
517 this%config%bodies(i)%stiff_geom%cutoff_coef, 3.5_rp)
518 case default
519 call neko_error("ALE: Invalid stiff_geom.decay_profile: " // &
520 trim(this%config%bodies(i)%stiff_geom%decay_profile))
521 end select
522
523 select case (trim(this%config%bodies(i)%stiff_geom%type))
524 case ('cylinder', 'sphere')
525 call json_get(body_sub, 'stiff_geom.center', tmp_vec, &
526 expected_size = 3)
527 this%config%bodies(i)%stiff_geom%center = tmp_vec
528
529 call json_get(body_sub, 'stiff_geom.radius', &
530 this%config%bodies(i)%stiff_geom%radius)
531 case ('cheap_dist')
532 call json_get(body_sub, 'stiff_geom.stiff_dist', &
533 this%config%bodies(i)%stiff_geom%stiff_dist)
534 case ('box')
535 call neko_error("ALE: stiff_geom.type 'box' not yet" // &
536 " implemented.")
537 case default
538 call neko_error("ALE: Invalid stiff_geom.type: " // &
539 trim(this%config%bodies(i)%stiff_geom%type))
540 end select
541 elseif (import_base_shapes) then
542 ! do nothing.
543 else
544 call neko_error("ALE: Body '" // &
545 trim(this%config%bodies(i)%name) // &
546 "' must have 'stiff_geom' definition.")
547 end if
548
549 ! Initialize the pivots.
550 call init_pivot_state(this%ale_pivot(i), this%config%bodies(i))
551
552 call this%base_shapes(i)%init(coef%dof, &
553 "phi_" // trim(this%config%bodies(i)%name))
554 call field_rzero(this%base_shapes(i))
555
556 ! Create Ghost Trackers for numerically forming the rotation matrix
557 ! of each body.
558 ! Basis X (Pivot + 1.0 in X)
559 this%ghost_handles(1, i) = this%request_tracker( &
560 this%config%bodies(i)%rot_center + [1.0_rp, 0.0_rp, 0.0_rp], &
561 this%config%bodies(i)%id)
562 ! Basis Y (Pivot + 1.0 in Y)
563 this%ghost_handles(2, i) = this%request_tracker( &
564 this%config%bodies(i)%rot_center + [0.0_rp, 1.0_rp, 0.0_rp], &
565 this%config%bodies(i)%id)
566
567 call neko_log%message('Registered Body : ' // &
568 trim(this%config%bodies(i)%name))
569
570 ! Logging Stiff Body
571 call neko_log%message(' ')
572 if (associated(this%user_ale_base_shapes, &
574 (.not. import_base_shapes)) then
575 write(log_buf, '(A,A)') ' Stiff Type : ', &
576 trim(this%config%bodies(i)%stiff_geom%type)
577 call neko_log%message(log_buf)
578 write(log_buf, '(A,ES18.11,A,A,A,ES10.4)') ' Gain : ', &
579 this%config%bodies(i)%stiff_geom%gain, ' | Profile: ', &
580 trim(this%config%bodies(i)%stiff_geom%decay_profile), &
581 ' | Cutoff: ', this%config%bodies(i)%stiff_geom%cutoff_coef
582 call neko_log%message(log_buf)
583 select case (trim(this%config%bodies(i)%stiff_geom%type))
584 case ('cylinder', 'sphere')
585 write(log_buf, '(A,3(ES23.15,1X))') ' Center :', &
586 this%config%bodies(i)%stiff_geom%center
587 call neko_log%message(log_buf)
588 write(log_buf, '(A,ES23.15)') ' Radius :', &
589 this%config%bodies(i)%stiff_geom%radius
590 call neko_log%message(log_buf)
591 case ('cheap_dist')
592 write(log_buf, '(A,ES23.15)') ' Stiff Dist:', &
593 this%config%bodies(i)%stiff_geom%stiff_dist
594 call neko_log%message(log_buf)
595 end select
596 end if
597 call neko_log%message(' ')
598
599 ! Logging Oscillation
600 has_builtin_osc = any(abs(this%config%bodies(i)%osc_amp) .gt. 0.0_rp)
601
602 if (has_builtin_osc) then
603 if (has_user_rigid_kin .or. has_user_mesh_vel) then
604 call neko_log%message(' Oscillation : ' // &
605 'X(t) = Amp*sin(2*pi*Freq*t) + User')
606 write(log_buf, '(A,3(ES18.11,1X))') ' Amp :', &
607 this%config%bodies(i)%osc_amp
608 call neko_log%message(log_buf)
609 write(log_buf, '(A,3(ES18.11,1X))') ' Freq :', &
610 this%config%bodies(i)%osc_freq
611 call neko_log%message(log_buf)
612 else
613 call neko_log%message(' Oscillation : ' // &
614 'X(t) = Amp*sin(2*pi*Freq*t)')
615 write(log_buf, '(A,3(ES18.11,1X))') ' Amp :', &
616 this%config%bodies(i)%osc_amp
617 call neko_log%message(log_buf)
618 write(log_buf, '(A,3(ES18.11,1X))') ' Freq :', &
619 this%config%bodies(i)%osc_freq
620 call neko_log%message(log_buf)
621 end if
622 else
623 if (has_user_rigid_kin .or. has_user_mesh_vel) then
624 call neko_log%message(' Oscillation : User-defined')
625 else
626 call neko_log%message(' Oscillation : None')
627 end if
628 end if
629 call neko_log%message(' ')
630
631 ! Logging Rotation
632 has_builtin_rot = (trim(this%config%bodies(i)%rotation_type) &
633 /= 'user')
634
635 if (trim(this%config%bodies(i)%rotation_type) .eq. 'user') then
636
637 call neko_log%message(' Rotation Type: User-defined')
638
639 elseif (has_builtin_rot) then
640
641 ! Check parameters active
642 is_rot_active = .false.
643 select case (trim(this%config%bodies(i)%rotation_type))
644 case ('harmonic')
645 is_rot_active = any(abs(this%config%bodies(i)%rot_amp_degree) &
646 .gt. 0.0_rp)
647 case ('ramp')
648 is_rot_active = any(abs(this%config%bodies(i)%ramp_omega0) &
649 .gt. 0.0_rp)
650 case ('smooth_step')
651 is_rot_active = &
652 (abs(this%config%bodies(i)%target_rot_angle_deg) &
653 .gt. 0.0_rp)
654 end select
655
656 if (is_rot_active) then
657 ! Harmonic
658 if (trim(this%config%bodies(i)%rotation_type) &
659 .eq. 'harmonic') then
660 if (has_user_rigid_kin .or. has_user_mesh_vel) then
661 call neko_log%message(' Rotation : ' // &
662 'Theta(t) = Amp*sin(2*pi*Freq*t) + User')
663 else
664 call neko_log%message(' Rotation : ' // &
665 'Theta(t) = Amp*sin(2*pi*Freq*t)')
666 end if
667 write(log_buf, '(A,3(ES18.11,1X))') ' Amp (deg) :', &
668 this%config%bodies(i)%rot_amp_degree
669 call neko_log%message(log_buf)
670 write(log_buf, '(A,3(ES18.11,1X))') ' Freq :', &
671 this%config%bodies(i)%rot_freq
672 call neko_log%message(log_buf)
673
674 ! Ramp
675 elseif (trim(this%config%bodies(i)%rotation_type) &
676 .eq. 'ramp') then
677 if (has_user_rigid_kin .or. has_user_mesh_vel) then
678 call neko_log%message(' Rotation : ' // &
679 'Omega(t) = Omega0*(1 - exp(-4.6*t/t0)) + User')
680 else
681 call neko_log%message(' Rotation : ' // &
682 'Omega(t) = Omega0*(1 - exp(-4.6*t/t0))')
683 end if
684 write(log_buf, '(A,3(ES18.11,1X))') ' Omega0 :', &
685 this%config%bodies(i)%ramp_omega0
686 call neko_log%message(log_buf)
687 write(log_buf, '(A,3(ES18.11,1X))') ' t0 :', &
688 this%config%bodies(i)%ramp_t0
689 call neko_log%message(log_buf)
690
691 ! Smooth Step
692 elseif (trim(this%config%bodies(i)%rotation_type) &
693 .eq. 'smooth_step') then
694 if (has_user_rigid_kin .or. has_user_mesh_vel) then
695 call neko_log%message(' Rotation : ' // &
696 'Smooth Step Control + User')
697 else
698 call neko_log%message(' Rotation : ' // &
699 'Smooth Step Control')
700 end if
701 write(log_buf, '(A,I10)') ' Rotation Axis :', &
702 this%config%bodies(i)%rotation_axis
703 call neko_log%message(log_buf)
704 write(log_buf, '(A,ES18.11)') ' Target Rot ' // &
705 'Angle (deg) :', &
706 this%config%bodies(i)%target_rot_angle_deg
707 call neko_log%message(log_buf)
708 write(log_buf, '(A,4(ES18.11,1X))') &
709 ' Control Times [t0, t1, t2, t3] :', &
710 this%config%bodies(i)%step_control_times
711 call neko_log%message(log_buf)
712 end if
713 else
714 if (has_user_rigid_kin .or. has_user_mesh_vel) then
715 call neko_log%message(' Rotation Type: User-defined')
716 else
717 call neko_log%message(' Rotation Type: None')
718 end if
719 end if
720
721 end if
722
723 ! Logging Pivot
724 call neko_log%message(' ')
725 call neko_log%message(' Pivot Type : ' // &
726 trim(this%config%bodies(i)%rotation_center_type))
727
728 write(log_buf, '(A,3(ES18.11,1X))') ' Init Pivot:', &
729 this%config%bodies(i)%rot_center
730 call neko_log%message(log_buf)
731 call neko_log%message(' ')
732
733 end do
734 else
735 call neko_error("ALE: No 'ale bodies' found in case file!")
736 end if
737
738 if (this%config%nbodies .gt. 1 .and. (.not. import_base_shapes)) then
739 call this%phi_total%init(coef%dof, "phi_total")
740 call field_rzero(this%phi_total)
741 end if
742
743 ! Check to be sure moving no_slip ids belong to an ALE body
744 do i = 1, n_moving_zones
745 z = moving_zone_ids(i)
746 found_zone = .false.
747 j = 1
748 do while ((.not. found_zone) .and. (j .le. this%config%nbodies))
749 if (any(this%config%bodies(j)%zone_indices .eq. z)) then
750 found_zone = .true.
751 end if
752 j = j + 1
753 end do
754 if (.not. found_zone) then
755 write(log_buf_l, '(A,I0,A)') &
756 "ALE: zone index ", z, &
757 " has BC no_slip with moving: true, " // &
758 "but it is not registered in ALE bodies."
759 call neko_error(trim(log_buf_l))
760 end if
761 end do
762
763 ! Any id registered in ALE bodies must have
764 ! no_slip with moving: true in BCs.
765 do j = 1, this%config%nbodies
766 if (allocated(this%config%bodies(j)%zone_indices)) then
767 do i = 1, size(this%config%bodies(j)%zone_indices)
768 z = this%config%bodies(j)%zone_indices(i)
769 found_zone = .false.
770 if (n_moving_zones .gt. 0) then
771 if (any(moving_zone_ids(1:n_moving_zones) .eq. z)) then
772 found_zone = .true.
773 end if
774 end if
775 if (.not. found_zone) then
776 write(log_buf_l, '(A,I0,A,A)') &
777 "ALE: zone index ", z, &
778 " is registered in ALE bodies, ", &
779 "but the BC is not no_slip with moving: true."
780 call neko_error(trim(log_buf_l))
781 end if
782 end do
783 end if
784 end do
785
786 ! Check no zone ID is assigned to more than one ALE body.
787 do j = 1, this%config%nbodies
788 if (allocated(this%config%bodies(j)%zone_indices)) then
789 do i = 1, size(this%config%bodies(j)%zone_indices)
790 z = this%config%bodies(j)%zone_indices(i)
791
792 do k = j + 1, this%config%nbodies
793 if (allocated(this%config%bodies(k)%zone_indices)) then
794 if (any(this%config%bodies(k)%zone_indices .eq. z)) then
795 write(log_buf_l, '(A,I0,A,A,A,A,A)') &
796 "ALE: zone index ", z, &
797 " is assigned to multiple bodies ('", &
798 trim(this%config%bodies(j)%name), "' and '", &
799 trim(this%config%bodies(k)%name), "')."
800 call neko_error(trim(log_buf_l))
801 end if
802 end if
803 end do
804
805 end do
806 end if
807 end do
808
809 ! Find the smooth blending function for mesh displacement.
810 call this%solve_base_mesh_displacement(coef, json, import_base_shapes, &
811 abstol, ksp_solver, ksp_max_iter, &
812 precon_type, precon_params, res_monitor)
813
814 ! If we are restarting, we skip this. It will be handled
815 ! properly by chkp file.
816 if (.not. json%valid_path('case.restart_file')) then
817 t_init%t = 0.0_rp
818 t_init%tstep = 0
819 t_init%dt = 0.0_rp
820 call this%update_mesh_velocity(coef, t_init)
821 end if
822
823 call this%wm_x_lag%init(this%wm_x, 2)
824 call this%wm_y_lag%init(this%wm_y, 2)
825 call this%wm_z_lag%init(this%wm_z, 2)
826
827 if (allocated(moving_zone_ids)) deallocate(moving_zone_ids)
828 if (allocated(bc_type)) deallocate(bc_type)
829 if (allocated(zone_indices)) deallocate(zone_indices)
830 if (allocated(ksp_solver)) deallocate(ksp_solver)
831 if (allocated(precon_type)) deallocate(precon_type)
832 if (allocated(tmp_str)) deallocate(tmp_str)
833 if (allocated(tmp_vec)) deallocate(tmp_vec)
834
835 ! Performing mesh_preview.
836 call this%mesh_preview(coef, json)
837
838 ! Register checkpoint fields
839 call this%register_checkpoint_fields(coef, chkp)
840
841 call neko_log%end_section()
842 end subroutine ale_manager_init
843
848 subroutine solve_base_mesh_displacement(this, coef, json, &
849 import_base_shapes, abstol, ksp_solver, ksp_max_iter, precon_type, &
850 precon_params, res_monitor)
851 class(ale_manager_t), intent(inout), target :: this
852 class(ax_t), allocatable :: Ax
853 class(ksp_t), allocatable :: ksp
854 class(pc_t), allocatable :: pc
855 type(coef_t), intent(inout) :: coef
856 type(json_file), intent(inout) :: json
857 logical, intent(in) :: import_base_shapes
858 real(kind=rp), intent(in) :: abstol
859 logical, intent(in) :: res_monitor
860 character(len=*), intent(in) :: ksp_solver, precon_type
861 integer, intent(in) :: ksp_max_iter
862 type(json_file), intent(inout) :: precon_params
863 type(file_t) :: phi_file
864 type(field_t), pointer :: phi_ptr => null()
865 type(field_t) :: rhs_field
866 type(field_t) :: corr_field
867 type(ksp_monitor_t) :: monitor(1)
868 real(kind=rp) :: sample_start_time, sample_end_time
869 real(kind=rp) :: sample_time
870 character(len=LOG_SIZE) :: log_buf
871 integer :: n, i, m, k, ierr, body_idx, z_idx
872 integer :: j
873 real(kind=rp), allocatable :: h1_restore(:, :, :, :)
874 real(kind=rp), allocatable :: h2_restore(:, :, :, :)
875 type(zero_dirichlet_t) :: bc_active_body
876 type(zero_dirichlet_t) :: bc_inactive_body
877 type(bc_list_t) :: bcloc
878 type(bc_list_t) :: bcloc_zeros_only
879 type(json_file) :: body_sub
880 character(len=256) :: phi_fname
881 character(len=:), allocatable :: tmp_str
882
883
884 if (.not. this%active) return
885 if (.not. this%has_moving_boundary) return
886 if (this%config%nbodies .eq. 0) return
887
888 if (import_base_shapes) then
889 call neko_log%message(" ")
890 call neko_log%message("Importing ALE base shapes" // &
891 " (skipping Laplace solve)...")
892
893 do body_idx = 1, this%config%nbodies
894
895 call json_extract_item(json, 'case.fluid.ale.bodies', &
896 body_idx, body_sub)
897
898 call json_get(body_sub, 'base_shape_import_file', tmp_str)
899 phi_fname = tmp_str
900
901 phi_ptr => this%base_shapes(body_idx)
902
903 ! Load the field
904 call import_fields(fname = trim(phi_fname), p = phi_ptr)
905
906 call neko_log%message(" Loaded: " // &
907 trim(phi_fname) // &
908 " for body: " // &
909 trim(this%config%bodies(body_idx)%name))
910 end do
911
912 return
913 end if
914
915 call neko_log%message(" ")
916 call neko_log%message("Starting base mesh motion solve ...")
917 n = coef%dof%size()
918
919 call ax_helm_factory(ax, full_formulation = .false.)
920 call krylov_solver_factory(ksp, n, ksp_solver, &
921 ksp_max_iter, abstol, monitor = res_monitor)
922 call ale_precon_factory(pc, ksp, coef, coef%dof, &
923 coef%gs_h, this%bc_list, precon_type, precon_params)
924
925 ! Save original h1/h2
926 h1_restore = coef%h1
927 h2_restore = coef%h2
928
929 call rhs_field%init(coef%dof)
930 call corr_field%init(coef%dof)
931
932
933 ! User Defined Base Shapes (Skip Solver).
934 if (.not. associated(this%user_ale_base_shapes, &
936 call neko_log%message(" Using user-defined base shapes " // &
937 "(skipping Laplace solve)")
938
939 ! Call User Hook (Populates this%base_shapes)
940 call this%user_ale_base_shapes(this%base_shapes)
941
942 ! Compute phi_total (Sum of all user shapes)
943 if (this%config%nbodies .gt. 1) then
944 call field_rzero(this%phi_total)
945 do body_idx = 1, this%config%nbodies
946 call field_add2(this%phi_total, this%base_shapes(body_idx), n)
947 end do
948 end if
949
950 ! Output Shapes
951 if (this%config%if_output_phi) then
952 ! Individual Bodies
953 do body_idx = 1, this%config%nbodies
954 call phi_file%init('phi_' // &
955 trim(this%config%bodies(body_idx)%name) // '.fld', &
956 precision = rp)
957 select type (ft => phi_file%file_type)
958 type is (fld_file_t)
959 ft%skip_pressure = .false.
960 end select
961 call phi_file%write(this%base_shapes(body_idx))
962 call phi_file%free()
963 call neko_log%message(' phi_' // &
964 trim(this%config%bodies(body_idx)%name) // '.fld saved.')
965 end do
966
967 ! Total
968 if (this%config%nbodies .gt. 1) then
969 call neko_log%message(" phi_total.fld saved.")
970 select type (ft => phi_file%file_type)
971 type is (fld_file_t)
972 ft%skip_pressure = .false.
973 end select
974 call phi_file%init('phi_total.fld', precision = rp)
975 call phi_file%write(this%phi_total)
976 call phi_file%free()
977 end if
978 end if
979 else
980 ! Standard Laplace Solve (Requires Stiffness)
981
982 ! Compute Stiffness
983 call compute_stiffness_ale(coef, this%config)
984
985 ! Output Stiffness if requested (for diagnostic)
986 if (this%config%if_output_stiffness) then
987 rhs_field%x = coef%h1
988 call phi_file%init('stiffness.fld')
989 call phi_file%write(rhs_field)
990 call phi_file%free()
991 call field_rzero(rhs_field)
992 end if
993
994 ! Loop over bodies and Solve Laplace
995 do body_idx = 1, this%config%nbodies
996 call mpi_barrier(neko_comm, ierr)
997 sample_start_time = mpi_wtime()
998 call neko_log%message(" Solving laplace for body: " // &
999 trim(this%config%bodies(body_idx)%name))
1000
1001 call bc_active_body%init_from_components(coef)
1002 call bc_inactive_body%init_from_components(coef)
1003
1004 ! Mark zones
1005 do j = 1, size(this%config%bodies(body_idx)%zone_indices)
1006 z_idx = this%config%bodies(body_idx)%zone_indices(j)
1007 call bc_active_body%mark_zone(coef%msh%labeled_zones(z_idx))
1008 end do
1009
1010 do i = 1, this%config%nbodies
1011 if (i /= body_idx) then
1012 do j = 1, size(this%config%bodies(i)%zone_indices)
1013 z_idx = this%config%bodies(i)%zone_indices(j)
1014 call bc_inactive_body%mark_zone(&
1015 coef%msh%labeled_zones(z_idx))
1016 end do
1017 end if
1018 end do
1019
1020 call bc_active_body%finalize()
1021 call bc_inactive_body%finalize()
1022
1023 ! The Full list for the solver (Freeze everything to 0 correction)
1024 call bcloc%init()
1025 call bcloc%append(this%bc_fixed)
1026 call bcloc%append(bc_active_body)
1027 call bcloc%append(bc_inactive_body)
1028
1029 ! The "Zeros Only" list for the field (Reset other boundaries)
1030 call bcloc_zeros_only%init()
1031 call bcloc_zeros_only%append(this%bc_fixed)
1032 call bcloc_zeros_only%append(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 bcloc_zeros_only%apply_scalar(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 bcloc%apply_scalar(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, bcloc, 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 bcloc%free()
1098 call bcloc_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: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...