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