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
79 end type output_controller_t
80
81contains
82
93 subroutine output_controller_init(this, time_end, size, time_start, &
94 write_at_start)
95 class(output_controller_t), intent(inout) :: this
96 integer, intent(in), optional :: size
97 real(kind=dp), intent(in) :: time_end
98 real(kind=dp), intent(in), optional :: time_start
99 logical, intent(in), optional :: write_at_start
100 character(len=LOG_SIZE) :: log_buf
101 integer :: n, i
102
103 call this%free()
104
105 if (present(size)) then
106 n = size
107 else
108 n = 1
109 end if
110
111 allocate(this%output_list(n))
112 allocate(this%controllers(n))
113
114 do i = 1, n
115 this%output_list(i)%ptr => null()
116 end do
117
118 this%size = n
119 this%n = 0
120 this%time_end = time_end
121
122 if (present(time_start)) then
123 this%time_start = time_start
124 else
125 this%time_start = 0.0_dp
126 end if
127
128 if (present(write_at_start)) then
129 this%write_at_start = write_at_start
130 else
131 this%write_at_start = .true.
132 end if
133
134 end subroutine output_controller_init
135
137 subroutine output_controller_free(this)
138 class(output_controller_t), intent(inout) :: this
139
140 if (allocated(this%output_list)) then
141 deallocate(this%output_list)
142 end if
143 if (allocated(this%controllers)) then
144 deallocate(this%controllers)
145 end if
146
147 this%n = 0
148 this%size = 0
149
150 end subroutine output_controller_free
151
165 subroutine output_controller_add(this, out, write_par, write_control, &
166 start_time, write_at_start)
167 class(output_controller_t), intent(inout) :: this
168 class(output_t), intent(inout), target :: out
169 real(kind=dp), intent(in) :: write_par
170 character(len=*), intent(in) :: write_control
171 real(kind=dp), optional, intent(in) :: start_time
172 logical, optional, intent(in) :: write_at_start
173 real(kind=dp) :: start_time_
174 logical :: write_at_start_
175 type(output_ptr_t), allocatable :: tmp(:)
176 type(time_based_controller_t), allocatable :: tmp_ctrl(:)
177 character(len=LOG_SIZE) :: log_buf
178 integer :: n, nexecutions
179 class(*), pointer :: ft
180
181 if (present(start_time)) then
182 start_time_ = start_time
183 else
184 start_time_ = this%time_start
185 end if
186
187 if (present(write_at_start)) then
188 write_at_start_ = write_at_start
189 else
190 write_at_start_ = this%write_at_start
191 end if
192
193
194 if (this%n .ge. this%size) then
195 allocate(tmp(this%size * 2))
196 tmp(1:this%size) = this%output_list
197 call move_alloc(tmp, this%output_list)
198
199 allocate(tmp_ctrl(this%size * 2))
200 tmp_ctrl(1:this%size) = this%controllers
201 call move_alloc(tmp_ctrl, this%controllers)
202
203 this%size = this%size * 2
204 end if
205
206
207 this%n = this%n + 1
208 n = this%n
209 this%output_list(this%n)%ptr => out
210
211 if (trim(write_control) .eq. "org") then
212 this%controllers(n) = this%controllers(1)
213 else
214 call this%controllers(n)%init(start_time_, this%time_end, &
215 write_control, write_par, write_at_start_, &
216 direction = this%time_end - this%time_start)
217 end if
218
219 ! The code below only prints to console
220 call neko_log%section('Adding write output')
221 call neko_log%message('File name : '// &
222 trim(this%output_list(this%n)%ptr%file_%file_type%get_fname()))
223 call neko_log%message('Write control : '//trim(write_control))
224 if (.not. this%controllers(n)%never) then
225 write(log_buf, '(A,ES13.6)') 'First write at : ', &
226 this%controllers(n)%next_time()
227 if (trim(write_control) .ne. 'tsteps') call neko_log%message(log_buf)
228 end if
229
230 ! Show the output precision if we are outputting an fld file
231 select type (ft => out%file_%file_type)
232 type is (fld_file_t)
233 if (ft%dp_precision) then
234 call neko_log%message('Output precision : double')
235 else
236 call neko_log%message('Output precision : single')
237 end if
238 end select
239
240 if (trim(write_control) .eq. 'simulationtime') then
241 write(log_buf, '(A,ES13.6)') 'Writes per time unit (Freq.): ', &
242 this%controllers(n)%frequency
243 call neko_log%message(log_buf)
244 write(log_buf, '(A,ES13.6)') 'Time between writes: ', &
245 this%controllers(n)%time_interval
246 call neko_log%message(log_buf)
247 else if (trim(write_control) .eq. 'nsamples') then
248 write(log_buf, '(A,I13)') 'Total samples: ', int(write_par)
249 call neko_log%message(log_buf)
250 write(log_buf, '(A,ES13.6)') 'Writes per time unit (Freq.): ', &
251 this%controllers(n)%frequency
252 call neko_log%message(log_buf)
253 write(log_buf, '(A,ES13.6)') 'Time between writes: ', &
254 this%controllers(n)%time_interval
255 call neko_log%message(log_buf)
256 else if (trim(write_control) .eq. 'tsteps') then
257 write(log_buf, '(A,I13)') 'Time step interval: ', int(write_par)
258 call neko_log%message(log_buf)
259 else if (trim(write_control) .eq. 'org') then
260 write(log_buf, '(A)') &
261 'Write control not set, defaulting to first output settings'
262 call neko_log%message(log_buf)
263 end if
264
265 call neko_log%end_section()
266 end subroutine output_controller_add
267
273 subroutine output_controller_execute(this, time, ifforce)
274 class(output_controller_t), intent(inout) :: this
275 type(time_state_t), intent(in) :: time
276 logical, intent(in), optional :: ifforce
277 real(kind=dp) :: sample_start_time, sample_end_time
278 real(kind=dp) :: sample_time
279 character(len=LOG_SIZE) :: log_buf
280 character(len=1024) :: output_fname
281 integer :: i, ierr
282 logical :: force, write_output, write_output_test
283
284 if (present(ifforce)) then
285 force = ifforce
286 else
287 force = .false.
288 end if
289
290 call profiler_start_region('Output controller', 22)
291 !Do we need this Barrier?
292 call mpi_barrier(neko_comm, ierr)
293 sample_start_time = mpi_wtime()
294
295 write_output = .false.
296 ! Determine if at least one output needs to be written
297 ! We should not need this extra select block, and it works great
298 ! without it for GNU, Intel and NEC, but breaks horribly on Cray
299 ! (>11.0.x) when using high opt. levels.
300 select type (samp => this)
301 type is (output_controller_t)
302 do i = 1, samp%n
303 if (this%controllers(i)%check(time, force)) then
304 write_output = .true.
305 exit
306 end if
307 end do
308 end select
309
310 if (write_output) then
311 call neko_log%section('Writer output ')
312 end if
313
314 ! Loop through the outputs and write if necessary.
315 ! We should not need this extra select block, and it works great
316 ! without it for GNU, Intel and NEC, but breaks horribly on Cray
317 ! (>11.0.x) when using high opt. levels.
318 select type (samp => this)
319 type is (output_controller_t)
320 do i = 1, this%n
321 if (this%controllers(i)%check(time, force)) then
322 output_fname = &
323 samp%output_list(i)%ptr%file_%file_type% &
324 get_next_output_fname()
325 call neko_log%message('File name : '//trim(output_fname))
326
327 write(log_buf, '(A,I6)') 'Output number :', &
328 int(this%controllers(i)%nexecutions)
329 call neko_log%message(log_buf)
330 call neko_log%flush()
331
332 call samp%output_list(i)%ptr%sample(time%t)
333
334 call this%controllers(i)%register_execution(time)
335 end if
336 end do
337 class default
338 call neko_error('Invalid output_controller output list')
339 end select
340
341 call mpi_barrier(neko_comm, ierr)
342 sample_end_time = mpi_wtime()
343
344 sample_time = sample_end_time - sample_start_time
345 if (write_output) then
346 write(log_buf, '(A16,1x,F12.6,A,F9.6)') 'Writing at time:', time%t, &
347 ' Output time (s): ', sample_time
348 call neko_log%message(log_buf)
349 call neko_log%end_section()
350 call neko_log%flush()
351 end if
352 call profiler_end_region('Output controller', 22)
353 end subroutine output_controller_execute
354
357 subroutine output_controller_set_counter(this, time)
358 class(output_controller_t), intent(inout) :: this
359 type(time_state_t), intent(in) :: time
360 character(len=LOG_SIZE) :: log_buf
361 character(len=1024) :: output_fname
362 integer :: i, nexecutions
363 logical :: file_exists
364
365 do i = 1, this%n
366 call this%controllers(i)%set_counter(time)
367 ! A step based schedule cannot be reconstructed from the time alone,
368 ! so the file counter of such an output is left alone.
369 if (this%controllers(i)%nsteps .eq. 0) then
370 nexecutions = this%controllers(i)%nexecutions
371 call this%output_list(i)%ptr%set_counter(-1)
372 call this%output_list(i)%ptr%set_start_counter(nexecutions)
373 end if
374
375 ! The file counter is set to the number of writes scheduled up to the
376 ! restart time, so it points past the files of the previous run. It
377 ! does not when the run repeats an interval it has already covered, or
378 ! when the output frequency was changed, and the files of the previous
379 ! run are then overwritten.
380 if (this%controllers(i)%never) cycle
381 file_exists = .false.
382 output_fname = &
383 this%output_list(i)%ptr%file_%file_type%get_next_output_fname()
384 if (pe_rank .eq. 0) then
385 inquire(file = trim(output_fname), exist = file_exists)
386 end if
387 if (file_exists) then
388 write(log_buf, '(A,A)') 'Overwriting from: ', trim(output_fname)
389 call neko_log%warning(log_buf)
390 end if
391 end do
392
393 end subroutine output_controller_set_counter
394
397 subroutine output_controller_set_write_count(this, counter)
398 class(output_controller_t), intent(inout) :: this
399 integer, intent(in) :: counter
400 integer :: i
401
402 do i = 1, this%n
403 this%controllers(i)%nexecutions = counter
404 call this%output_list(i)%ptr%set_counter(counter)
405 call this%output_list(i)%ptr%set_start_counter(counter)
406 end do
407
409
410
411end 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:91
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.
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:53
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.