Neko 1.99.3
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
ale_rigid_kinematics.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!
33
39 use num_types, only : rp
40 use math, only : pi, math_dstepf
41 use time_state, only : time_state_t
43 use math, only : rzero
44 use logger, only : neko_log
45
46 implicit none
47 private
48
50 public :: update_pivot_location
51 public :: init_pivot_state
52 public :: advance_point_tracker
54
56 type, public :: stiffness_geometry_t
58 character(len=32) :: type = 'cheap_dist'
60 character(len=32) :: decay_profile = 'gaussian'
62 real(kind=rp) :: gain
64 real(kind=rp) :: center(3)
66 real(kind=rp) :: dims(3) = 1.0_rp
68 real(kind=rp) :: rot_angle_deg_cw = 0.0_rp
70 real(kind=rp) :: radius
72 real(kind=rp) :: stiff_dist
75 real(kind=rp) :: cutoff_coef
77
79 type, public :: ale_body_t
80 integer :: id
81 character(len=32) :: name = 'body'
82 type(stiffness_geometry_t) :: stiff_geom
84 real(kind=rp) :: osc_amp(3) = 0.0_rp
85 real(kind=rp) :: osc_freq(3) = 0.0_rp
87 character(len=32) :: rotation_center_type = 'relative'
88 character(len=32) :: rotation_type
89 real(kind=rp) :: rot_amp_degree(3) = 0.0_rp
90 real(kind=rp) :: rot_freq(3) = 0.0_rp
91 real(kind=rp) :: rot_center(3) = 0.0_rp
95 real(kind=rp) :: step_control_times(4) = 0.0_rp
96 real(kind=rp) :: target_rot_angle_deg = 0.0_rp
97 integer :: rotation_axis = 3
99 real(kind=rp) :: ramp_omega0(3) = 0.0_rp
100 real(kind=rp) :: ramp_t0(3) = 1.0_rp
102 integer, allocatable :: zone_indices(:)
103 end type ale_body_t
104
106 type, public :: ale_config_t
109 character(len=32) :: stiffness_type = 'built-in'
110 logical :: if_output_phi = .true.
111 logical :: if_output_stiffness = .false.
113 type(ale_body_t), allocatable :: bodies(:)
114 integer :: nbodies = 0
115 end type ale_config_t
116
118 type, public :: body_kinematics_t
119 real(kind=rp) :: vel_trans(3) = 0.0_rp
120 real(kind=rp) :: vel_ang(3) = 0.0_rp
121 real(kind=rp) :: center(3) = 0.0_rp
122 end type body_kinematics_t
123
125 type, public :: pivot_state_t
126 real(kind=rp) :: pos(3) = 0.0_rp
127 real(kind=rp) :: vel_lag(3, 3) = 0.0_rp
128 real(kind=rp) :: vel(3) = 0.0_rp
129
130 end type pivot_state_t
131
133 type, public :: point_tracker_t
134 real(kind=rp) :: pos(3) = 0.0_rp
135 real(kind=rp) :: vel_lag(3, 3) = 0.0_rp
136 integer :: body_id = 0
137 end type point_tracker_t
138
139contains
140
142 subroutine init_pivot_state(pivot, body_conf)
143 type(pivot_state_t), intent(out) :: pivot
144 type(ale_body_t), intent(in) :: body_conf
145 pivot%pos = body_conf%rot_center
146 pivot%vel_lag = 0.0_rp
147 pivot%vel = 0.0_rp
148 end subroutine init_pivot_state
149
152 subroutine ab_integrate_point_pos(pos, vel_lag, current_vel, time, nadv)
153 real(kind=rp), intent(inout) :: pos(3)
154 real(kind=rp), intent(inout) :: vel_lag(3, 3)
155 real(kind=rp), intent(in) :: current_vel(3)
156 type(time_state_t), intent(in) :: time
157 integer, intent(in) :: nadv
158
159 type(ab_time_scheme_t) :: ab_scheme_obj
160 real(kind=rp) :: ab_coeffs(4), dt_history(10)
161 integer :: j
162
163 call rzero(ab_coeffs, 4)
164 dt_history(1) = time%dt
165 dt_history(2) = time%dtlag(1)
166 dt_history(3) = time%dtlag(2)
167 call ab_scheme_obj%compute_coeffs(ab_coeffs, dt_history, nadv)
168
169 pos(1) = pos(1) + time%dt * ab_coeffs(1) * current_vel(1)
170 pos(2) = pos(2) + time%dt * ab_coeffs(1) * current_vel(2)
171 pos(3) = pos(3) + time%dt * ab_coeffs(1) * current_vel(3)
172
173 do j = 2, nadv
174 pos(1) = pos(1) + time%dt * ab_coeffs(j) * vel_lag(1, j - 1)
175 pos(2) = pos(2) + time%dt * ab_coeffs(j) * vel_lag(2, j - 1)
176 pos(3) = pos(3) + time%dt * ab_coeffs(j) * vel_lag(3, j - 1)
177 end do
178
179 ! Shift history
180 do j = 3, 2, -1
181 vel_lag(1, j) = vel_lag(1, j - 1)
182 vel_lag(2, j) = vel_lag(2, j - 1)
183 vel_lag(3, j) = vel_lag(3, j - 1)
184 end do
185
186 ! Store current velocity
187 vel_lag(1, 1) = current_vel(1)
188 vel_lag(2, 1) = current_vel(2)
189 vel_lag(3, 1) = current_vel(3)
190
191 end subroutine ab_integrate_point_pos
192
195 subroutine advance_point_tracker(tracker, current_vel, time, nadv)
196 type(point_tracker_t), intent(inout) :: tracker
197 real(kind=rp), intent(in) :: current_vel(3)
198 type(time_state_t), intent(in) :: time
199 integer, intent(in) :: nadv
200
201 call ab_integrate_point_pos(tracker%pos, tracker%vel_lag, &
202 current_vel, time, nadv)
203
204 end subroutine advance_point_tracker
205
208 subroutine compute_body_kinematics_built_in(kinematics, body_conf, time)
209 type(body_kinematics_t), intent(out) :: kinematics
210 type(ale_body_t), intent(in) :: body_conf
211 type(time_state_t), intent(in) :: time
212
213 real(kind=rp) :: w_scalar, amp_scalar, ex_term
214 real(kind=rp) :: t_curr, t0, t1, t2, t3, target_rad, tau, current_omega
215 integer :: i
216
217 ! Oscillation
218 kinematics%vel_trans = 0.0_rp
219 do i = 1, 3
220 if (abs(body_conf%osc_amp(i)) > 0.0_rp) then
221 w_scalar = body_conf%osc_freq(i) * 2.0_rp * pi
222 kinematics%vel_trans(i) = body_conf%osc_amp(i) * w_scalar * &
223 cos(w_scalar * time%t)
224 end if
225 end do
226
227 ! Rotation
228 kinematics%vel_ang = 0.0_rp
229 select case (trim(body_conf%rotation_type))
230 case ('smooth_step')
231 if (abs(body_conf%target_rot_angle_deg) > 0.0_rp) then
232 t_curr = time%t
233 t0 = body_conf%step_control_times(1)
234 t1 = body_conf%step_control_times(2)
235 t2 = body_conf%step_control_times(3)
236 t3 = body_conf%step_control_times(4)
237 target_rad = body_conf%target_rot_angle_deg * (pi / 180.0_rp)
238 current_omega = 0.0_rp
239
240 if (t_curr < t0) then
241 current_omega = 0.0_rp
242 elseif (t_curr < t1) then ! Rise
243 if (t1 > t0) then
244 tau = (t_curr - t0) / (t1 - t0)
245 current_omega = target_rad * math_dstepf(tau) * &
246 (1.0_rp / (t1 - t0))
247 end if
248 elseif (t_curr < t2) then ! Hold
249 current_omega = 0.0_rp
250 elseif (t_curr < t3) then ! Fall
251 if (t3 > t2) then
252 tau = (t_curr - t2) / (t3 - t2)
253 current_omega = -1.0_rp * target_rad * math_dstepf(tau) * &
254 (1.0_rp / (t3 - t2))
255 end if
256 end if
257 kinematics%vel_ang(body_conf%rotation_axis) = current_omega
258 end if
259
260 ! ramp motion based on Benton, S. I., and M. R. Visbal.
261 ! "The onset of dynamic stall at a high, transitional Reynolds number."
262 ! Journal of Fluid Mechanics 861 (2019): 860-885.
263 ! and
264 ! Visbal, Miguel R., and J. S. Shang.
265 ! "Investigation of the flow structure around a rapidly pitching airfoil."
266 ! AIAA journal 27.8 (1989): 1044-1051.
267 case ('ramp')
268 do i = 1, 3
269 if (body_conf%ramp_t0(i) > 0.0_rp .and. &
270 abs(body_conf%ramp_omega0(i)) > 0.0_rp) then
271 ex_term = exp(-4.6_rp * time%t / body_conf%ramp_t0(i))
272 kinematics%vel_ang(i) = body_conf%ramp_omega0(i) * &
273 (1.0_rp - ex_term)
274 end if
275 end do
276
277 case ('harmonic')
278 do i = 1, 3
279 if (abs(body_conf%rot_amp_degree(i)) > 0.0_rp) then
280 amp_scalar = body_conf%rot_amp_degree(i) * (pi / 180.0_rp)
281 w_scalar = body_conf%rot_freq(i) * 2.0_rp * pi
282 kinematics%vel_ang(i) = w_scalar * amp_scalar * &
283 cos(w_scalar * time%t)
284 end if
285 end do
286 end select
287
289
291 subroutine update_pivot_location(pivot, pivot_loc, pivot_vel, time, &
292 nadv, body_conf)
293 type(pivot_state_t), intent(inout) :: pivot
294 real(kind=rp), intent(out) :: pivot_loc(3)
295 real(kind=rp), intent(in) :: pivot_vel(3)
296 type(time_state_t), intent(in) :: time
297 integer, intent(in) :: nadv
298 type(ale_body_t), intent(in) :: body_conf
299 real(kind=rp) :: omega_osc(3), trans_disp(3)
300
301 select case (trim(body_conf%rotation_center_type))
302
303 ! This mode uses vel_trans to move the pivot by integrating the velocity
304 case ('relative')
305
306 if (time%tstep > 0) then
307 call ab_integrate_point_pos(pivot%pos, pivot%vel_lag, &
308 pivot_vel, time, nadv)
309 end if
310 pivot_loc = pivot%pos
311
312 ! Mostly for validation. It's too restrictive.
313 ! maybe I remove it totally in future.
314 case ('relative_sin')
315 omega_osc = body_conf%osc_freq * 2.0_rp * pi
316 trans_disp(1) = body_conf%osc_amp(1) * sin(omega_osc(1) * time%t)
317 trans_disp(2) = body_conf%osc_amp(2) * sin(omega_osc(2) * time%t)
318 trans_disp(3) = body_conf%osc_amp(3) * sin(omega_osc(3) * time%t)
319 pivot_loc = body_conf%rot_center + trans_disp
320 pivot%pos = pivot_loc
321 end select
322 end subroutine update_pivot_location
323
324end module ale_rigid_kinematics
Adam-Bashforth scheme for time integration.
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 advance_point_tracker(tracker, current_vel, time, nadv)
Updates the point tracker's position and velocity history using AB time integration based on the curr...
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.
Logging routines.
Definition log.f90:34
type(log_t), public neko_log
Global log stream.
Definition log.f90:79
Definition math.f90:60
real(kind=rp), parameter, public pi
Definition math.f90:75
real(kind=rp) function, public math_dstepf(x)
Derivative of math_stepf with respect to x: d(stepf)/dx.
Definition math.f90:1556
subroutine, public rzero(a, n)
Zero a real vector.
Definition math.f90:206
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:12
Module with things related to the simulation time.
Explicit Adam-Bashforth scheme for time integration.
Configuration for a single moving body.
Calculated Kinematics for a body at current time.
State history for time-integration of pivots.
Type for a tracked point linked to a body.
A struct that contains all info about the time, expand as needed.