38 use json_module,
only : json_file
43 use mpi_f08,
only : mpi_min, mpi_max, mpi_in_place, mpi_allreduce, &
44 mpi_double_precision, mpi_integer
58 logical :: is_variable_dt = .false.
59 real(kind=
dp) :: cfl_trg = 0.0_dp
60 real(kind=
dp) :: cfl_avg = 0.0_dp
61 real(kind=
dp) :: init_dt = huge(0.0_dp)
62 real(kind=
dp) :: max_dt = 0.0_dp
63 real(kind=
dp) :: min_dt = 0.0_dp
64 integer :: max_update_frequency = 0
65 integer :: min_update_frequency = 0
68 integer :: dt_last_change = -1
69 real(kind=
dp) :: alpha = 0.0_dp
70 real(kind=
dp) :: max_dt_increase_factor = 0.0_dp
71 real(kind=
dp) :: min_dt_decrease_factor = 0.0_dp
72 real(kind=
dp) :: dev_tol = 0.0_dp
75 logical :: exact_output_time = .false.
77 real(kind=
dp) :: dt_previous = 0.0_dp
96 type(json_file),
intent(inout) :: params
97 integer :: flags_any(2), flags_all(2), ierr
99 this%dt_last_change = -1
101 this%is_variable_dt, .false.)
102 if (this%is_variable_dt)
then
104 this%cfl_trg, 0.4_dp)
106 this%init_dt, huge(0.0_dp))
108 this%max_dt, huge(0.0_dp))
112 this%max_update_frequency, 0)
114 this%min_update_frequency, huge(0))
118 this%max_dt_increase_factor, 1.2_dp)
120 this%min_dt_decrease_factor, 0.5_dp)
122 this%dev_tol, 0.2_dp)
126 this%exact_output_time, .false.)
127 if (this%exact_output_time .and. .not. this%is_variable_dt)
then
128 call neko_error(
'exact_output_time requires variable_timestep')
136 flags_any = merge(1, 0, [this%is_variable_dt, this%exact_output_time])
137 flags_all = flags_any
138 call mpi_allreduce(mpi_in_place, flags_any, 2, mpi_integer, &
140 call mpi_allreduce(mpi_in_place, flags_all, 2, mpi_integer, &
142 if (any(flags_any .ne. flags_all))
then
143 call neko_error(
'variable_timestep and exact_output_time must &
144 &each be set in all the coupled cases of an MPMD run, or in &
160 real(kind=
dp),
intent(in) :: cfl
164 if (cfl .gt. 0.0_dp) dt = min(this%cfl_trg / cfl * time%dt, dt)
165 dt =
max(min(dt, this%max_dt), this%min_dt)
184 real(kind=
dp),
intent(in) :: cfl
185 real(kind=
dp) :: dt_old, scaling_factor, global_min_dt
186 character(len=LOG_SIZE) :: log_buf
189 this%dt_previous = time%dt
192 if (.not. this%is_variable_dt)
return
195 if (this%dt_last_change .eq. 0)
then
199 if (this%dt_last_change .eq. -1)
then
203 time%dt = this%first_dt(time, cfl)
204 this%dt_last_change = 0
207 this%dt_previous = time%dt
211 this%cfl_avg = this%alpha * cfl + (1 - this%alpha) * this%cfl_avg
213 if (abs(this%cfl_avg - this%cfl_trg) .ge. this%dev_tol * this%cfl_trg &
214 .and. this%dt_last_change .ge. this%max_update_frequency &
215 .or. this%dt_last_change .ge. this%min_update_frequency)
then
218 if (
abscmp(cfl, 0.0_dp))
then
220 scaling_factor = this%max_dt_increase_factor
221 else if (this%cfl_trg/cfl .ge. 1)
then
223 scaling_factor = min(this%max_dt_increase_factor, this%cfl_trg/cfl)
226 scaling_factor =
max(this%min_dt_decrease_factor, this%cfl_trg/cfl)
230 time%dt = scaling_factor * dt_old
231 time%dt =
max(min(time%dt, this%max_dt), this%min_dt)
233 write(log_buf,
'(A,E15.7,1x,A,E15.7)') &
234 'Average CFL:', this%cfl_avg, &
235 'Target CFL:', this%cfl_trg
238 write(log_buf,
'(A,E15.7,1x,A,E15.7)')
'Old dt:', dt_old, &
242 this%dt_last_change = 0
245 this%dt_last_change = this%dt_last_change + 1
251 global_min_dt = time%dt
252 call mpi_allreduce(mpi_in_place, global_min_dt, 1, &
256 if (time%dt .gt. global_min_dt)
then
257 time%dt = global_min_dt
258 this%dt_last_change = 0
289 real(kind=
dp),
intent(in) :: time_to_next
290 real(kind=
dp) :: direction, dt_prev, lo, remaining, remaining_end, x
291 integer(kind=i8) :: n
294 if (.not. (this%exact_output_time .and. this%is_variable_dt))
return
296 direction = sign(1.0_dp, time%dt)
297 dt_prev = abs(this%dt_previous)
298 lo = this%min_dt_decrease_factor * dt_prev
301 remaining_end = direction * (time%end_time - time%t)
302 if (remaining_end .le. time_to_next + lo)
then
303 remaining = remaining_end
304 else if (time_to_next .ge. lo)
then
305 remaining = time_to_next
310 x = remaining / abs(time%dt)
314 n =
max(1_i8, floor(x, kind =
i8))
319 if (x * x .gt.
real(n,
dp) *
real(n + 1_i8,
dp)) n = n + 1_i8
327 n =
max(n, ceiling(remaining / min(this%max_dt_increase_factor * &
330 time%dt = direction * remaining /
real(n,
dp)
335 call mpi_allreduce(mpi_in_place, time%dt, 1, mpi_double_precision, &
340 if (abs(abs(time%dt) - dt_prev) .gt.
landing_tol * dt_prev)
then
341 this%dt_last_change = 0
Retrieves a parameter by name or assigns a provided default value. In the latter case also adds the m...
type(mpi_comm), public neko_global_comm
pure logical function, public is_mpmd()
Whether this is one of several simulations coupled in an MPMD run, that is, whether Neko's communicat...
Utilities for retrieving parameters from the case files.
type(log_t), public neko_log
Global log stream.
integer, parameter, public log_size
integer, parameter, public i8
integer, parameter, public dp
Module with things related to the simulation time.
Implements type time_step_controller.
real(kind=dp), parameter, public landing_tol
Relative tolerance on the number of steps up to a scheduled time, so that round-off in the accumulate...
subroutine time_step_controller_init(this, params)
Constructor.
real(kind=dp), parameter max_landing_steps
Largest number of steps up to a scheduled time that is still fitted.
pure real(kind=dp) function time_step_controller_first_dt(this, time, cfl)
The first time step of a variable time step run: the one giving the target CFL number,...
subroutine time_step_controller_land(this, time, time_to_next)
Fit the time step so that the next scheduled time, or the end time, is reached exactly.
subroutine time_step_controller_set_dt(this, time, cfl)
Set new dt based on cfl if requested.
A struct that contains all info about the time, expand as needed.
Provides a tool to set time step dt.