Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
time_based_controller.f90
Go to the documentation of this file.
1! Copyright (c) 2023, 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 : dp, i8
36 use utils, only : neko_error
37 use time_state, only : time_state_t
38 implicit none
39 private
40
49 real(kind=dp), public, parameter :: time_tol = 0.1_dp
50
53 real(kind=dp), public, parameter :: span_tol = 1.0e-9_dp
54
88 real(kind=dp) :: frequency = 0.0_dp
90 real(kind=dp) :: time_interval = 0.0_dp
92 integer :: nsteps = 0
94 real(kind=dp) :: start_time = 0.0_dp
96 real(kind=dp) :: end_time = 0.0_dp
98 integer :: nexecutions = 0
100 logical :: never = .false.
103 character(len=:), allocatable :: control_mode
105 real(kind=dp) :: control_value = 0.0_dp
108 real(kind=dp) :: anchor_time = 0.0_dp
113 logical :: write_at_start = .true.
115 integer(kind=i8) :: first_index = 0
117 logical :: start_is_scheduled = .false.
121 logical :: start_pending = .false.
124 integer :: next_index = 0
127 real(kind=dp) :: direction = 1.0_dp
130 integer :: tstep_offset = 0
134 integer :: last_tstep = -1
135
136 contains
138 procedure, pass(this) :: init => time_based_controller_init
140 procedure, pass(this) :: free => time_based_controller_free
142 procedure, pass(this) :: check => time_based_controller_check
144 procedure, pass(this) :: register_execution => &
147 procedure, pass(this) :: set_counter => &
150 procedure, pass(this) :: next_time => time_based_controller_next_time
152 procedure, pass(this) :: tolerance => time_based_controller_tolerance
154
155contains
156
172 subroutine time_based_controller_init(this, start_time, end_time, &
173 control_mode, control_value, write_at_start, anchor_time, direction)
174 class(time_based_controller_t), intent(inout) :: this
175 real(kind=dp), intent(in) :: start_time
176 real(kind=dp), intent(in) :: end_time
177 character(len=*), intent(in) :: control_mode
178 real(kind=dp), intent(in) :: control_value
179 logical, intent(in), optional :: write_at_start
180 real(kind=dp), intent(in), optional :: anchor_time
181 real(kind=dp), intent(in), optional :: direction
182 real(kind=dp) :: span, offset
183
184 call this%free()
185
186 this%start_time = start_time
187 this%end_time = end_time
188 this%control_mode = control_mode
189 this%control_value = control_value
190
191 if (present(write_at_start)) then
192 this%write_at_start = write_at_start
193 else
194 this%write_at_start = .true.
195 end if
196
197 if (present(direction)) then
198 this%direction = sign(1.0_dp, direction)
199 else
200 this%direction = sign(1.0_dp, end_time - start_time)
201 end if
202 ! Length of the interval covered, positive also for a run backwards.
203 span = this%direction * (end_time - start_time)
204
205 if (trim(control_mode) .eq. 'simulationtime') then
206 if (control_value .le. 0.0_dp) then
207 call neko_error("The output interval must be positive")
208 end if
209 this%time_interval = control_value
210 this%frequency = 1.0_dp / this%time_interval
211 this%nsteps = 0
212 else if (trim(control_mode) .eq. 'nsamples') then
213 if (control_value .le. 0.0_dp) then
214 call neko_error("nsamples must be positive")
215 end if
216 if (span .le. 0.0_dp) then
217 call neko_error("nsamples requires the output to start before the &
218 &end of the simulation")
219 end if
220
221 this%frequency = control_value / span
222 this%time_interval = 1.0_dp / this%frequency
223 this%nsteps = 0
224 ! The samples divide the interval from start_time to end_time, so
225 ! they are counted from start_time.
226 this%anchor_time = start_time
227 else if (trim(control_mode) .eq. 'tsteps') then
228 if (control_value .lt. 1.0_dp) then
229 call neko_error("The output interval in time steps must be at &
230 &least 1")
231 end if
232 this%nsteps = int(control_value)
233 ! if the timestep will be variable, we cannot compute these.
234 this%frequency = 0.0_dp
235 this%time_interval = 0.0_dp
236 else if (trim(control_mode) .eq. 'never') then
237 this%never = .true.
238 else
239 call neko_error("The control parameter must be simulationtime, nsamples&
240 & tsteps, or never, but received "//trim(control_mode))
241 end if
242
243 if (present(anchor_time)) this%anchor_time = anchor_time
244
245 ! Find the index of the first scheduled time, and whether start_time is
246 ! itself one of the scheduled times.
247 this%first_index = 0
248 this%start_is_scheduled = .true.
249 this%start_pending = .false.
250
251 if (this%time_interval .gt. 0.0_dp) then
252 offset = this%direction * (start_time - this%anchor_time) / &
253 this%time_interval
254 this%first_index = ceiling(offset - span_tol * max(1.0_dp, &
255 abs(offset)), kind = i8)
256 this%start_is_scheduled = abs(real(this%first_index, dp) - offset) &
257 .le. span_tol * max(1.0_dp, abs(offset))
258 if (this%start_is_scheduled .and. .not. this%write_at_start) then
259 this%first_index = this%first_index + 1_i8
260 end if
261 this%start_pending = this%write_at_start .and. &
262 .not. this%start_is_scheduled
263 end if
264
265 if (this%nsteps .gt. 0 .and. .not. this%write_at_start) then
266 ! The first scheduled step of a step based schedule is the first step
267 ! of the run. Skip it, as start_time is skipped above.
268 this%next_index = 1
269 else
270 this%next_index = 0
271 end if
272
273 end subroutine time_based_controller_init
274
277 class(time_based_controller_t), intent(inout) :: this
278
279 if (allocated(this%control_mode)) then
280 deallocate(this%control_mode)
281 end if
282
283 this%frequency = 0.0_dp
284 this%time_interval = 0.0_dp
285 this%nsteps = 0
286 this%start_time = 0.0_dp
287 this%end_time = 0.0_dp
288 this%nexecutions = 0
289 this%never = .false.
290 this%control_value = 0.0_dp
291 this%write_at_start = .true.
292 this%anchor_time = 0.0_dp
293 this%first_index = 0
294 this%start_is_scheduled = .false.
295 this%start_pending = .false.
296 this%next_index = 0
297 this%direction = 1.0_dp
298 this%tstep_offset = 0
299 this%last_tstep = -1
300 end subroutine time_based_controller_free
301
314 function time_based_controller_check(this, time, force) result(check)
315 class(time_based_controller_t), intent(in) :: this
316 type(time_state_t), intent(in) :: time
317 logical, intent(in), optional :: force
318 logical :: check
319 logical :: ifforce
320 real(kind=dp) :: progress, tol, t_next, t_start, t_end
321 integer :: nstep
322
323 if (present(force)) then
324 ifforce = force
325 else
326 ifforce = .false.
327 end if
328
329 check = .false.
330
331 ! At most one execution per time step.
332 if (this%last_tstep .eq. time%tstep) return
333
334 ! Nothing is scheduled, but an execution can still be forced.
335 if (this%never .and. .not. ifforce) return
336
337 progress = this%direction * (time%t - this%anchor_time)
338 t_start = this%direction * (this%start_time - this%anchor_time)
339 tol = this%tolerance(time%dt)
340
341 ! Nothing is executed before start_time.
342 if (progress .lt. t_start - tol) return
343
344 if (ifforce) then
345 check = .true.
346 else if (this%nsteps .gt. 0) then
347 nstep = time%tstep - this%tstep_offset
348 check = nstep .ge. this%next_index * this%nsteps
349 else if (this%start_pending) then
350 ! The execution at start_time, which is not one of the scheduled
351 ! times.
352 check = .true.
353 else
354 t_end = this%direction * (this%end_time - this%anchor_time)
355 t_next = real(this%first_index + this%next_index, dp) * &
356 this%time_interval
357 ! Nothing is scheduled after end_time. The condition is on the
358 ! scheduled time and not on the current time, so a step that
359 ! overshoots end_time still performs the execution scheduled for it.
360 if (t_next .gt. t_end + span_tol * max(abs(t_end), &
361 this%time_interval)) return
362 check = progress .ge. t_next - tol
363 end if
364
365 end function time_based_controller_check
366
373 pure function next_index_after(this, time) result(index)
374 class(time_based_controller_t), intent(in) :: this
375 type(time_state_t), intent(in) :: time
376 integer :: index
377 real(kind=dp) :: progress, tol
378
379 if (this%nsteps .gt. 0) then
380 index = (time%tstep - this%tstep_offset) / this%nsteps + 1
381 else if (this%time_interval .gt. 0.0_dp) then
382 progress = this%direction * (time%t - this%anchor_time)
383 tol = this%tolerance(time%dt)
384 index = int(floor((progress + tol) / this%time_interval, kind = i8) &
385 - this%first_index) + 1
386 else
387 ! Nothing is scheduled.
388 index = this%next_index
389 end if
390
391 ! The result is not limited from below by the current `next_index` on
392 ! purpose: a scheduled execution always gives a larger index, and a
393 ! forced execution before the next scheduled time must leave
394 ! `next_index` unchanged.
395 index = max(index, 0)
396
397 end function next_index_after
398
402 class(time_based_controller_t), intent(inout) :: this
403 type(time_state_t), intent(in) :: time
404
405 this%nexecutions = this%nexecutions + 1
406 this%start_pending = .false.
407 this%next_index = next_index_after(this, time)
408 this%last_tstep = time%tstep
409
411
420 class(time_based_controller_t), intent(inout) :: this
421 type(time_state_t), intent(in) :: time
422 real(kind=dp) :: progress, tol, dt, t_start, t_first
423 integer :: n_passed
424
425 if (this%never) return
426
427 if (this%nsteps .gt. 0) then
428 ! `tstep` is not stored in the checkpoint, so a step based schedule
429 ! cannot be continued. Start a new one from the restart step, without
430 ! executing at the restart step itself.
431 this%tstep_offset = time%tstep
432 this%next_index = 1
433 this%start_pending = .false.
434 this%last_tstep = time%tstep
435 return
436 end if
437
438 ! The size of the step that produced the checkpoint, which is the step
439 ! `check` used at that time, and not the step of the new run.
440 dt = time%dt
441 if (abs(time%dtlag(1)) .gt. 0.0_dp) dt = time%dtlag(1)
442
443 progress = this%direction * (time%t - this%anchor_time)
444 t_start = this%direction * (this%start_time - this%anchor_time)
445 tol = this%tolerance(dt)
446
447 if (progress .lt. t_start - tol) then
448 ! The run has not reached start_time.
449 this%next_index = 0
450 this%nexecutions = 0
451 else
452 n_passed = int(floor((progress + tol) / this%time_interval, &
453 kind = i8) - this%first_index) + 1
454 this%next_index = max(n_passed, 0)
455 this%nexecutions = this%next_index
456 if (this%start_pending) then
457 ! The execution at start_time was performed by the previous run.
458 ! It is counted separately unless the first scheduled time was
459 ! within tolerance of start_time, in which case the same execution
460 ! was registered for both (see next_index_after).
461 t_first = real(this%first_index, dp) * this%time_interval
462 if (t_first .gt. t_start + tol) then
463 this%nexecutions = this%nexecutions + 1
464 end if
465 end if
466 this%start_pending = .false.
467 end if
468
469 ! Anything scheduled for the restart time was executed by the run that
470 ! wrote the checkpoint.
471 this%last_tstep = time%tstep
472
474
478 pure function time_based_controller_tolerance(this, dt) result(tol)
479 class(time_based_controller_t), intent(in) :: this
480 real(kind=dp), intent(in) :: dt
481 real(kind=dp) :: tol
482
483 tol = time_tol * abs(dt)
484 if (this%time_interval .gt. 0.0_dp) then
485 tol = min(tol, time_tol * this%time_interval)
486 end if
487
489
493 pure function time_based_controller_next_time(this) result(t)
494 class(time_based_controller_t), intent(in) :: this
495 real(kind=dp) :: t
496
497 if (this%never .or. this%nsteps .gt. 0) then
498 t = this%end_time
499 else if (this%start_pending) then
500 t = this%start_time
501 else
502 t = this%anchor_time + this%direction * &
503 real(this%first_index + this%next_index, dp) * this%time_interval
504 end if
505
507
508end module time_based_controller
double real
integer, parameter, public i8
Definition num_types.f90:7
integer, parameter, public dp
Definition num_types.f90:10
Contains the time_based_controller_t type.
subroutine time_based_controller_free(this)
Destructor.
subroutine time_based_controller_set_counter(this, time)
Set next_index to the first scheduled time not yet reached, and nexecutions to the number of executio...
pure integer function next_index_after(this, time)
The number of scheduled times at or before the given time, counted from first_index....
pure real(kind=dp) function time_based_controller_next_time(this)
The time of the next scheduled execution.
subroutine time_based_controller_register_execution(this, time)
Increment nexecutions and advance next_index past the current time.
pure real(kind=dp) function time_based_controller_tolerance(this, dt)
The tolerance used when comparing the simulation time to a scheduled execution time.
real(kind=dp), parameter, public span_tol
Relative tolerance for round-off in k * time_interval when comparing a scheduled time to start_time a...
subroutine time_based_controller_init(this, start_time, end_time, control_mode, control_value, write_at_start, anchor_time, direction)
Constructor.
logical function time_based_controller_check(this, time, force)
Check if the execution should be performed.
real(kind=dp), parameter, public time_tol
Tolerance for comparing the simulation time to a scheduled time, as a fraction of the time step....
Module with things related to the simulation time.
Utilities.
Definition utils.f90:35
A utility type for determining whether an action should be executed based on the current time value....
A struct that contains all info about the time, expand as needed.
#define max(a, b)
Definition tensor.cu:40