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