Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
output_controller.f90
Go to the documentation of this file.
1! Copyright (c) 2020-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 output, only : output_t, output_ptr_t
36 use fld_file, only : fld_file_t
37 use comm
38 use time_state, only : time_state_t
39 use logger, only : neko_log, log_size
40 use utils, only : neko_error
42 use num_types, only : dp
44 use mpi_f08, only : mpi_wtime, mpi_barrier
45 implicit none
46 private
47
48
53 type, public :: output_controller_t
55 type(output_ptr_t), allocatable :: output_list(:)
57 type(time_based_controller_t), allocatable :: controllers(:)
59 integer :: n
61 integer :: size
63 real(kind=dp) :: time_start
65 logical :: write_at_start = .true.
67 real(kind=dp) :: time_end
68 contains
70 procedure, pass(this) :: init => output_controller_init
72 procedure, pass(this) :: free => output_controller_free
74 procedure, pass(this) :: add => output_controller_add
76 procedure, pass(this) :: execute => output_controller_execute
78 procedure, pass(this) :: set_counter => output_controller_set_counter
80 procedure, pass(this) :: time_to_next => output_controller_time_to_next
81 end type output_controller_t
82
83contains
84
95 subroutine output_controller_init(this, time_end, size, time_start, &
96 write_at_start)
97 class(output_controller_t), intent(inout) :: this
98 integer, intent(in), optional :: size
99 real(kind=dp), intent(in) :: time_end
100 real(kind=dp), intent(in), optional :: time_start
101 logical, intent(in), optional :: write_at_start
102 character(len=LOG_SIZE) :: log_buf
103 integer :: n, i
104
105 call this%free()
106
107 if (present(size)) then
108 n = size
109 else
110 n = 1
111 end if
112
113 allocate(this%output_list(n))
114 allocate(this%controllers(n))
115
116 do i = 1, n
117 this%output_list(i)%ptr => null()
118 end do
119
120 this%size = n
121 this%n = 0
122 this%time_end = time_end
123
124 if (present(time_start)) then
125 this%time_start = time_start
126 else
127 this%time_start = 0.0_dp
128 end if
129
130 if (present(write_at_start)) then
131 this%write_at_start = write_at_start
132 else
133 this%write_at_start = .true.
134 end if
135
136 end subroutine output_controller_init
137
139 subroutine output_controller_free(this)
140 class(output_controller_t), intent(inout) :: this
141
142 if (allocated(this%output_list)) then
143 deallocate(this%output_list)
144 end if
145 if (allocated(this%controllers)) then
146 deallocate(this%controllers)
147 end if
148
149 this%n = 0
150 this%size = 0
151
152 end subroutine output_controller_free
153
167 subroutine output_controller_add(this, out, write_par, write_control, &
168 start_time, write_at_start)
169 class(output_controller_t), intent(inout) :: this
170 class(output_t), intent(inout), target :: out
171 real(kind=dp), intent(in) :: write_par
172 character(len=*), intent(in) :: write_control
173 real(kind=dp), optional, intent(in) :: start_time
174 logical, optional, intent(in) :: write_at_start
175 real(kind=dp) :: start_time_
176 logical :: write_at_start_
177 type(output_ptr_t), allocatable :: tmp(:)
178 type(time_based_controller_t), allocatable :: tmp_ctrl(:)
179 character(len=LOG_SIZE) :: log_buf
180 integer :: n, nexecutions
181 class(*), pointer :: ft
182
183 if (present(start_time)) then
184 start_time_ = start_time
185 else
186 start_time_ = this%time_start
187 end if
188
189 if (present(write_at_start)) then
190 write_at_start_ = write_at_start
191 else
192 write_at_start_ = this%write_at_start
193 end if
194
195
196 if (this%n .ge. this%size) then
197 allocate(tmp(this%size * 2))
198 tmp(1:this%size) = this%output_list
199 call move_alloc(tmp, this%output_list)
200
201 allocate(tmp_ctrl(this%size * 2))
202 tmp_ctrl(1:this%size) = this%controllers
203 call move_alloc(tmp_ctrl, this%controllers)
204
205 this%size = this%size * 2
206 end if
207
208
209 this%n = this%n + 1
210 n = this%n
211 this%output_list(this%n)%ptr => out
212
213 if (trim(write_control) .eq. "org") then
214 this%controllers(n) = this%controllers(1)
215 else
216 call this%controllers(n)%init(start_time_, this%time_end, &
217 write_control, write_par, write_at_start_, &
218 direction = this%time_end - this%time_start)
219 end if
220
221 ! The code below only prints to console
222 call neko_log%section('Adding write output')
223 call neko_log%message('File name : '// &
224 trim(this%output_list(this%n)%ptr%file_%file_type%get_fname()))
225 call neko_log%message('Write control : '//trim(write_control))
226 if (.not. this%controllers(n)%never) then
227 write(log_buf, '(A,ES13.6)') 'First write at : ', &
228 this%controllers(n)%next_time()
229 if (trim(write_control) .ne. 'tsteps') call neko_log%message(log_buf)
230 end if
231
232 ! Show the output precision if we are outputting an fld file
233 select type (ft => out%file_%file_type)
234 type is (fld_file_t)
235 if (ft%dp_precision) then
236 call neko_log%message('Output precision : double')
237 else
238 call neko_log%message('Output precision : single')
239 end if
240 end select
241
242 if (trim(write_control) .eq. 'simulationtime') then
243 write(log_buf, '(A,ES13.6)') 'Writes per time unit (Freq.): ', &
244 this%controllers(n)%frequency
245 call neko_log%message(log_buf)
246 write(log_buf, '(A,ES13.6)') 'Time between writes: ', &
247 this%controllers(n)%time_interval
248 call neko_log%message(log_buf)
249 else if (trim(write_control) .eq. 'nsamples') then
250 write(log_buf, '(A,I13)') 'Total samples: ', int(write_par)
251 call neko_log%message(log_buf)
252 write(log_buf, '(A,ES13.6)') 'Writes per time unit (Freq.): ', &
253 this%controllers(n)%frequency
254 call neko_log%message(log_buf)
255 write(log_buf, '(A,ES13.6)') 'Time between writes: ', &
256 this%controllers(n)%time_interval
257 call neko_log%message(log_buf)
258 else if (trim(write_control) .eq. 'tsteps') then
259 write(log_buf, '(A,I13)') 'Time step interval: ', int(write_par)
260 call neko_log%message(log_buf)
261 else if (trim(write_control) .eq. 'org') then
262 write(log_buf, '(A)') &
263 'Write control not set, defaulting to first output settings'
264 call neko_log%message(log_buf)
265 end if
266
267 call neko_log%end_section()
268 end subroutine output_controller_add
269
275 subroutine output_controller_execute(this, time, ifforce)
276 class(output_controller_t), intent(inout) :: this
277 type(time_state_t), intent(in) :: time
278 logical, intent(in), optional :: ifforce
279 real(kind=dp) :: sample_start_time, sample_end_time
280 real(kind=dp) :: sample_time
281 character(len=LOG_SIZE) :: log_buf
282 character(len=1024) :: output_fname
283 integer :: i, ierr
284 logical :: force, write_output, write_output_test
285
286 if (present(ifforce)) then
287 force = ifforce
288 else
289 force = .false.
290 end if
291
292 call profiler_start_region('Output controller', 22)
293 !Do we need this Barrier?
294 call mpi_barrier(neko_comm, ierr)
295 sample_start_time = mpi_wtime()
296
297 write_output = .false.
298 ! Determine if at least one output needs to be written
299 ! We should not need this extra select block, and it works great
300 ! without it for GNU, Intel and NEC, but breaks horribly on Cray
301 ! (>11.0.x) when using high opt. levels.
302 select type (samp => this)
303 type is (output_controller_t)
304 do i = 1, samp%n
305 if (this%controllers(i)%check(time, force)) then
306 write_output = .true.
307 exit
308 end if
309 end do
310 end select
311
312 if (write_output) then
313 call neko_log%section('Writer output ')
314 end if
315
316 ! Loop through the outputs and write if necessary.
317 ! We should not need this extra select block, and it works great
318 ! without it for GNU, Intel and NEC, but breaks horribly on Cray
319 ! (>11.0.x) when using high opt. levels.
320 select type (samp => this)
321 type is (output_controller_t)
322 do i = 1, this%n
323 if (this%controllers(i)%check(time, force)) then
324 output_fname = &
325 samp%output_list(i)%ptr%file_%file_type% &
326 get_next_output_fname()
327 call neko_log%message('File name : '//trim(output_fname))
328
329 write(log_buf, '(A,I6)') 'Output number :', &
330 int(this%controllers(i)%nexecutions)
331 call neko_log%message(log_buf)
332 call neko_log%flush()
333
334 call samp%output_list(i)%ptr%sample(time%t)
335
336 call this%controllers(i)%register_execution(time)
337 end if
338 end do
339 class default
340 call neko_error('Invalid output_controller output list')
341 end select
342
343 call mpi_barrier(neko_comm, ierr)
344 sample_end_time = mpi_wtime()
345
346 sample_time = sample_end_time - sample_start_time
347 if (write_output) then
348 write(log_buf, '(A16,1x,F12.6,A,F9.6)') 'Writing at time:', time%t, &
349 ' Output time (s): ', sample_time
350 call neko_log%message(log_buf)
351 call neko_log%end_section()
352 call neko_log%flush()
353 end if
354 call profiler_end_region('Output controller', 22)
355 end subroutine output_controller_execute
356
359 subroutine output_controller_set_counter(this, time)
360 class(output_controller_t), intent(inout) :: this
361 type(time_state_t), intent(in) :: time
362 character(len=LOG_SIZE) :: log_buf
363 character(len=1024) :: output_fname
364 integer :: i, nexecutions
365 logical :: file_exists
366
367 do i = 1, this%n
368 call this%controllers(i)%set_counter(time)
369 ! A step based schedule cannot be reconstructed from the time alone,
370 ! so the file counter of such an output is left alone.
371 if (this%controllers(i)%nsteps .eq. 0) then
372 nexecutions = this%controllers(i)%nexecutions
373 call this%output_list(i)%ptr%set_counter(-1)
374 call this%output_list(i)%ptr%set_start_counter(nexecutions)
375 end if
376
377 ! The file counter is set to the number of writes scheduled up to the
378 ! restart time, so it points past the files of the previous run. It
379 ! does not when the run repeats an interval it has already covered, or
380 ! when the output frequency was changed, and the files of the previous
381 ! run are then overwritten.
382 if (this%controllers(i)%never) cycle
383 file_exists = .false.
384 output_fname = &
385 this%output_list(i)%ptr%file_%file_type%get_next_output_fname()
386 if (pe_rank .eq. 0) then
387 inquire(file = trim(output_fname), exist = file_exists)
388 end if
389 if (file_exists) then
390 write(log_buf, '(A,A)') 'Overwriting from: ', trim(output_fname)
391 call neko_log%warning(log_buf)
392 end if
393 end do
394
395 end subroutine output_controller_set_counter
396
399 subroutine output_controller_set_write_count(this, counter)
400 class(output_controller_t), intent(inout) :: this
401 integer, intent(in) :: counter
402 integer :: i
403
404 do i = 1, this%n
405 this%controllers(i)%nexecutions = counter
406 call this%output_list(i)%ptr%set_counter(counter)
407 call this%output_list(i)%ptr%set_start_counter(counter)
408 end do
409
411
412
416 pure function output_controller_time_to_next(this, time) &
417 result(t)
418 class(output_controller_t), intent(in) :: this
419 type(time_state_t), intent(in) :: time
420 real(kind=dp) :: t
421 integer :: i
422
423 t = huge(0.0_dp)
424 do i = 1, this%n
425 t = min(t, this%controllers(i)%time_to_next(time))
426 end do
427
429
430end module output_controller
Definition comm.F90:1
integer, public pe_rank
MPI rank.
Definition comm.F90:59
type(mpi_comm), public neko_comm
MPI communicator.
Definition comm.F90:46
Module for file I/O operations.
Definition file.f90:34
NEKTON fld file format.
Definition fld_file.f90:35
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
integer, parameter, public dp
Definition num_types.f90:10
Implements output_controller_t
subroutine output_controller_execute(this, time, ifforce)
Query each of the controllers whether it is time to write, and if so, do so for the corresponding out...
subroutine output_controller_init(this, time_end, size, time_start, write_at_start)
Constructor.
subroutine output_controller_free(this)
Destructor.
subroutine output_controller_add(this, out, write_par, write_control, start_time, write_at_start)
Add an output out to the controller.
pure real(kind=dp) function output_controller_time_to_next(this, time)
The time until the next scheduled write that the time step should land on, the smallest over all outp...
subroutine output_controller_set_counter(this, time)
Set write counter based on time (after restart)
subroutine output_controller_set_write_count(this, counter)
Set write counter (after restart) explicitly.
Defines an output.
Definition output.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
Contains the time_based_controller_t type.
Module with things related to the simulation time.
Utilities.
Definition utils.f90:35
Interface for NEKTON fld files.
Definition fld_file.f90:66
Wrapper around an output_t pointer.
Definition output.f90:55
Abstract type defining an output type.
Definition output.f90:41
Centralized controller for a list of outputs.
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.