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