Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
utils.f90
Go to the documentation of this file.
1! Copyright (c) 2019-2021, 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!
35module utils
36 use, intrinsic :: iso_fortran_env, only : error_unit, output_unit
37 use iso_c_binding
38 use num_types, only: rp, dp
39 implicit none
40 private
41
42 integer, parameter :: neko_fname_len = 1024
43 integer, parameter :: neko_varname_len = 256
44
45 interface neko_error
46 module procedure neko_error_plain, neko_error_msg
47 end interface neko_error
48
49 interface read_duration
50 module procedure read_duration_scalar
51 module procedure read_duration_components
52 end interface read_duration
53
60
61 interface
62 function c_mkdir(path, mode) bind(C, name="mkdir")
63 import :: c_char, c_int
64 character(kind=c_char), dimension(*) :: path
65 integer(c_int), value :: mode
66 integer(c_int) :: c_mkdir
67 end function c_mkdir
68 end interface
69
70contains
71
73 pure function filename_suffix_pos(fname) result(suffix_pos)
74 character(len=*), intent(in) :: fname
75 integer :: suffix_pos
76 suffix_pos = scan(trim(fname), '.', back = .true.)
77 end function filename_suffix_pos
78
80 pure function filename_tslash_pos(fname) result(tslash_pos)
81 character(len=*), intent(in) :: fname
82 integer :: tslash_pos
83 tslash_pos = scan(trim(fname), '/', back = .true.)
84 end function filename_tslash_pos
85
87 subroutine filename_path(fname, path)
88 character(len=*), intent(in) :: fname
89 character(len=*), intent(out) :: path
90 integer :: tslash_pos
91
92 tslash_pos = filename_tslash_pos(fname)
93 if (tslash_pos .gt. 0) then
94 path = trim(fname(1:tslash_pos))
95 else
96 path = './'
97 end if
98
99 end subroutine filename_path
100
102 subroutine filename_name(fname, name)
103 character(len=*), intent(in) :: fname
104 character(len=*), intent(out) :: name
105 integer :: tslash_pos, suffix_pos, start, end
106
107 tslash_pos = filename_tslash_pos(fname)
108 suffix_pos = filename_suffix_pos(fname)
109 if (tslash_pos .eq. 0) then
110 start = 1
111 else
112 start = tslash_pos + 1
113 end if
114 if (suffix_pos .eq. 0) then
115 end = len_trim(fname)
116 else
117 end = suffix_pos - 1
118 end if
119 name = trim(fname(start:end))
120 end subroutine filename_name
121
123 subroutine filename_suffix(fname, suffix)
124 character(len=*) :: fname
125 character(len=*) :: suffix
126 suffix = trim(fname(filename_suffix_pos(fname) + 1:len_trim(fname)))
127 end subroutine filename_suffix
128
130 subroutine filename_split(fname, path, name, suffix)
131 character(len=*), intent(in) :: fname
132 character(len=*), intent(out) :: path, name, suffix
133 integer :: tslash_pos, suffix_pos
134
135 tslash_pos = filename_tslash_pos(fname)
136 suffix_pos = filename_suffix_pos(fname)
137
138 if (tslash_pos .gt. 0) then
139 path = trim(fname(1:tslash_pos))
140 else
141 path = './'
142 end if
143
144 if (suffix_pos .gt. 0) then
145 name = trim(fname(tslash_pos + 1:suffix_pos - 1))
146 suffix = trim(fname(suffix_pos:len_trim(fname)))
147 else
148 name = trim(fname(tslash_pos + 1:len_trim(fname)))
149 suffix = ''
150 end if
151
152 end subroutine filename_split
153
155 subroutine filename_chsuffix(fname, new_fname, new_suffix)
156 character(len=*) :: fname
157 character(len=*) :: new_fname
158 character(len=*) :: new_suffix
159 integer :: suffix_pos
160
161 suffix_pos = filename_suffix_pos(fname)
162 new_fname = trim(fname(1:suffix_pos))//new_suffix
163
164 end subroutine filename_chsuffix
165
173 recursive subroutine mkdir(path, mode)
174 character(len=*), intent(in) :: path
175 integer, intent(in), optional :: mode
176 integer :: slash_pos, i, path_len
177 character(kind=c_char), allocatable :: c_path(:)
178 integer(c_int) :: dir_mode, ierr
179
180 if (present(mode)) then
181 dir_mode = int(mode, kind=c_int)
182 else
183 dir_mode = int(o'777', kind=c_int)
184 end if
185
186 slash_pos = scan(path, '/', back = .true.)
187 if (slash_pos .gt. 0) then
188 call mkdir(trim(path(1:slash_pos-1)), dir_mode)
189 end if
190
191 path_len = len_trim(path)
192 allocate(c_path(path_len + 1))
193 do i = 1, path_len
194 c_path(i) = path(i:i)
195 end do
196 c_path(path_len + 1) = c_null_char
197
198 ierr = c_mkdir(c_path, dir_mode)
199 deallocate(c_path)
200 end subroutine mkdir
201
208 function extract_fld_file_index(fld_filename, default_index) result(index)
209 character(len=*), intent(in) :: fld_filename
210 integer, intent(in) :: default_index
211
212 character(len=80) :: suffix
213 integer :: index, fpos, i
214 logical :: valid
215
216 call filename_suffix(fld_filename, suffix)
217
218 valid = .true.
219
220 ! This value will be modified when reading the file name extension
221 ! e.g. "field0.f00035" will set sample_idx = 35
222 index = default_index
223
224 !
225 ! Try to extract the index of the field file from the suffix "fxxxxx"
226 !
227 fpos = scan(trim(suffix), 'f')
228 if (fpos .eq. 1) then
229 ! Make sure that the suffix only contains integers from 0 to 9
230 do i = 2, len(trim(suffix))
231 if (.not. (iachar(suffix(i:i)) >= iachar('0') &
232 .and. iachar(suffix(i:i)) <= iachar('9'))) then
233 valid = .false.
234 end if
235 end do
236 else
237 valid = .false.
238 end if
239
240 ! Must be exactly 6 characters long, i.e. an 'f' with 5 integers after
241 if (len(trim(suffix)) .ne. 6) valid = .false.
242
243 if (valid) read (suffix(2:), "(I5.5)") index
244
245 end function extract_fld_file_index
246
250 function split_string(string, delimiter) result(split_str)
251 character(len=*) :: string
252 character(len=*) :: delimiter
253 character(len=100), allocatable :: split_str(:)
254 integer :: length, i, i2, offset, j
255 i = 0
256 offset = 1
257 length = 1
258 if (len(trim(string)) .eq. 0) then
259 allocate(split_str(1))
260 split_str(1) = trim(string)
261 return
262 end if
263 do while (.true.)
264 i = scan(string(offset:), delimiter, back = .false.)
265 if (i .eq. 0) exit
266 length = length + 1
267 offset = offset + i
268 end do
269
270 allocate(split_str(length))
271 i = 0
272 j = 1
273 offset = 1
274 do while (.true.)
275 i2 = scan(trim(string(offset:)), delimiter, back = .false.)
276 if (i2 .eq. 0) then
277 split_str(j) = trim(string(offset:))
278 exit
279 end if
280 split_str(j) = trim(string(offset:offset+i2-2))
281 offset = offset+i2
282 j = j + 1
283 end do
284 end function split_string
285
288 pure function linear_index(i, j, k, l, lx, ly, lz) result(index)
289 integer, intent(in) :: i, j, k, l, lx, ly, lz
290 integer :: index
291
292 index = (i + lx * ((j - 1) + ly * ((k - 1) + lz * ((l - 1)))))
293 end function linear_index
294
297 pure function nonlinear_index(linear_index, lx, ly, lz) result(index)
298 integer, intent(in) :: linear_index, lx, ly, lz
299 integer :: index(4)
300 integer :: lin_idx
301 lin_idx = linear_index -1
302 index(4) = lin_idx/(lx*ly*lz)
303 index(3) = (lin_idx-(lx*ly*lz)*index(4))/(lx*ly)
304 index(2) = (lin_idx-(lx*ly*lz)*index(4)-(lx*ly)*index(3))/lx
305 index(1) = (lin_idx-(lx*ly*lz)*index(4)-(lx*ly)*index(3)-lx*index(2))
306 index(1) = index(1) + 1
307 index(2) = index(2) + 1
308 index(3) = index(3) + 1
309 index(4) = index(4) + 1
310
311 end function nonlinear_index
312
313 pure function index_is_on_facet(i, j, k, lx, ly, lz, facet) result(is_on)
314 integer, intent(in) :: i, j, k, lx, ly, lz, facet
315 logical :: is_on
316
317 is_on = .false.
318 select case (facet)
319 case (1)
320 if (i .eq. 1) is_on = .true.
321 case (2)
322 if (i .eq. lx) is_on = .true.
323 case (3)
324 if (j .eq. 1) is_on = .true.
325 case (4)
326 if (j .eq. ly) is_on = .true.
327 case (5)
328 if (k .eq. 1) is_on = .true.
329 case (6)
330 if (k .eq. lz) is_on = .true.
331 end select
332
333 end function index_is_on_facet
334
337 subroutine neko_error_plain(error_code)
338 integer, optional :: error_code
339
340 ! Persist buffered log output before aborting, such that the error
341 ! appears after any preceding log messages
342 flush(output_unit)
343
344 if (present(error_code)) then
345 write(error_unit, *) '*** ERROR ***', error_code
346 flush(error_unit)
347 error stop
348 else
349 write(error_unit, *) '*** ERROR ***'
350 flush(error_unit)
351 error stop
352 end if
353
354 end subroutine neko_error_plain
355
358 subroutine neko_error_msg(error_msg)
359 character(len=*) :: error_msg
360
361 ! Persist buffered log output before aborting, such that the error
362 ! appears after any preceding log messages
363 flush(output_unit)
364 write(error_unit, *) '*** ERROR: ', trim(error_msg), ' ***'
365 flush(error_unit)
366 error stop
367 end subroutine neko_error_msg
368
375 subroutine neko_type_error(base_type, wrong_type, known_types)
376 character(len=*), intent(in) :: base_type
377 character(len=*), intent(in) :: wrong_type
378 character(len=*), intent(in) :: known_types(:)
379 integer :: i
380
381 flush(output_unit)
382 write(error_unit, *) '*** ERROR WHEN SELECTING TYPE ***'
383 write(error_unit, *) 'Type ', wrong_type, ' does not exist for ', base_type
384 write(error_unit, *) 'Valid types are:'
385 do i = 1, size(known_types)
386 write(error_unit, *) " ", known_types(i)
387 end do
388 flush(error_unit)
389 error stop
390 end subroutine neko_type_error
391
392 subroutine neko_type_registration_error(base_type, wrong_type, known)
393 character(len=*), intent(in) :: base_type
394 character(len=*), intent(in) :: wrong_type
395 logical, intent(in) :: known
396
397 flush(output_unit)
398 write(error_unit, *) '*** ERROR WHEN REGISTERING TYPE ***'
399 write(error_unit, *) 'Type name ', wrong_type, &
400 ' conflicts with and already existing ', base_type, " type"
401 if (known) then
402 write(error_unit, *) 'Please rename your custom type.'
403 else
404 write(error_unit, *) 'The already existing type is also custom.' // &
405 ' Make all custom type names unique!'
406 end if
407 flush(error_unit)
408 error stop
409 end subroutine neko_type_registration_error
410
412 subroutine neko_warning(warning_msg)
413 character(len=*) :: warning_msg
414 write(output_unit, *) '*** WARNING: ', trim(warning_msg), ' ***'
415 flush(output_unit)
416 end subroutine neko_warning
417
423 function concat_string_array(array, sep, prepend) result(result)
424 character(len=*), intent(in) :: array(:)
425 character(len=*), intent(in) :: sep
426 logical, intent(in) :: prepend
427 character(:), allocatable :: result
428 integer :: i
429
430 result = trim(array(1))
431 do i = 2, size(array)
432 result = result // sep // trim(array(i))
433 end do
434
435 if (prepend) then
436 result = sep // result
437 end if
438
439 end function concat_string_array
440
447 subroutine read_duration_scalar(runtime_string, runtime_seconds, &
448 ierr)
449 character(len=*), intent(in) :: runtime_string
450 real(kind=rp), intent(inout) :: runtime_seconds
451 integer, optional, intent(out) :: ierr
452 real(kind=dp) :: parsed_seconds
453
454 if (present(ierr)) ierr = 0
455 if (len_trim(runtime_string) .eq. 0) return
456
457 parsed_seconds = read_duration_internal(runtime_string, ierr)
458
459 if (present(ierr)) then
460 if (ierr .ne. 0) return
461 end if
462
463 runtime_seconds = parsed_seconds
464 end subroutine read_duration_scalar
465
472 subroutine read_duration_components(runtime_string, runtime_values, ierr)
473 character(len=*), intent(in) :: runtime_string
474 real(kind=rp), intent(inout) :: runtime_values(:)
475 integer, optional, intent(out) :: ierr
476 integer, parameter :: i64 = selected_int_kind(18)
477 integer :: n_values
478 integer(kind=i64) :: total_whole, days, hours, minutes, seconds_whole
479 real(kind=dp) :: parsed_seconds, second_value, frac_seconds
480
481 if (present(ierr)) ierr = 0
482 if (len_trim(runtime_string) .eq. 0) return
483
484 n_values = size(runtime_values)
485 if (n_values .lt. 1 .or. n_values .gt. 4) then
486 call set_error_or_throw( &
487 'Error parsing duration: output array size must be 1 to 4', ierr)
488 return
489 end if
490
491 parsed_seconds = read_duration_internal(runtime_string, ierr)
492
493 if (present(ierr)) then
494 if (ierr .ne. 0) return
495 end if
496
497 total_whole = int(parsed_seconds, kind=i64)
498 frac_seconds = parsed_seconds - real(total_whole, kind=dp)
499 if (frac_seconds .lt. 0.0_dp) frac_seconds = 0.0_dp
500
501 days = total_whole / 86400_i64
502 total_whole = total_whole - 86400_i64 * days
503 hours = total_whole / 3600_i64
504 total_whole = total_whole - 3600_i64 * hours
505 minutes = total_whole / 60_i64
506 seconds_whole = total_whole - 60_i64 * minutes
507
508 second_value = real(seconds_whole, kind=dp) + frac_seconds
509 if (second_value .ge. 60.0_dp) then
510 second_value = second_value - 60.0_dp
511 minutes = minutes + 1_i64
512 if (minutes .ge. 60_i64) then
513 minutes = minutes - 60_i64
514 hours = hours + 1_i64
515 if (hours .ge. 24_i64) then
516 hours = hours - 24_i64
517 days = days + 1_i64
518 end if
519 end if
520 end if
521
522 select case (n_values)
523 case (4)
524 runtime_values(1) = real(days, kind=rp)
525 runtime_values(2) = real(hours, kind=rp)
526 runtime_values(3) = real(minutes, kind=rp)
527 runtime_values(4) = real(second_value, kind=rp)
528 case (3)
529 runtime_values(1) = real(days * 24_i64 + hours, kind=rp)
530 runtime_values(2) = real(minutes, kind=rp)
531 runtime_values(3) = real(second_value, kind=rp)
532 case (2)
533 runtime_values(1) = real((days * 24_i64 + hours) * 60_i64 + &
534 minutes, kind=rp)
535 runtime_values(2) = real(second_value, kind=rp)
536 case (1)
537 runtime_values(1) = real(parsed_seconds, kind=rp)
538 end select
539 end subroutine read_duration_components
540
542 real(kind=dp) function read_duration_internal(runtime_string, ierr) &
543 result(runtime_seconds)
544 character(len=*), intent(in) :: runtime_string
545 integer, optional, intent(out) :: ierr
546 character(len=:), allocatable :: time_string
547 real(kind=dp) :: parsed_seconds, read_real
548 logical :: has_minutes, has_hours, has_days
549 integer :: read_int, ios, sep
550
551 if (present(ierr)) ierr = 0
552
553 runtime_seconds = 0.0_dp
554 time_string = trim(adjustl(runtime_string))
555
556 sep = index(time_string, ':')
557 has_minutes = sep .gt. 0
558 has_hours = .false.
559 if (has_minutes .and. sep .lt. len_trim(time_string)) then
560 has_hours = index(time_string(sep + 1:len_trim(time_string)), ':') &
561 .gt. 0
562 end if
563 has_days = index(time_string, '-') .gt. 0
564
565 if ((has_days .and. .not. has_hours) .or. &
566 (has_hours .and. .not. has_minutes)) then
567 call set_error_or_throw('Error parsing duration: Bad format', ierr)
568 return
569 end if
570
571 parsed_seconds = 0.0_dp
572
573 ! Read the days field.
574 if (has_days) then
575 sep = index(time_string, '-')
576 read(time_string(1:sep - 1), *, iostat=ios) read_int
577 if (ios .ne. 0 .or. read_int .lt. 0) then
578 call set_error_or_throw( &
579 'Error parsing duration: Invalid days value', ierr)
580 return
581 end if
582
583 time_string = time_string(sep + 1:)
584 parsed_seconds = parsed_seconds + real(86400 * read_int, kind=dp)
585 end if
586
587 ! Read the hours.
588 if (has_hours) then
589 sep = index(time_string, ':')
590 read(time_string(1:sep - 1), *, iostat=ios) read_int
591 if (ios .ne. 0 .or. read_int .lt. 0 .or. &
592 (has_days .and. read_int .gt. 23)) then
593 call set_error_or_throw( &
594 'Error parsing duration: Invalid hours value', ierr)
595 return
596 end if
597
598 time_string = time_string(sep + 1:)
599 parsed_seconds = parsed_seconds + real(3600 * read_int, kind=dp)
600 end if
601
602 ! Read the minutes.
603 if (has_minutes) then
604 sep = index(time_string, ':')
605 read(time_string(1:sep - 1), *, iostat=ios) read_int
606 if (ios .ne. 0 .or. read_int .lt. 0 .or. &
607 (has_hours .and. read_int .gt. 59)) then
608 call set_error_or_throw( &
609 'Error parsing duration: Invalid minutes value', ierr)
610 return
611 end if
612
613 time_string = time_string(sep + 1:)
614 parsed_seconds = parsed_seconds + real(60 * read_int, kind=dp)
615 end if
616
617 ! Read the seconds.
618 read(time_string, *, iostat=ios) read_real
619 if (ios .ne. 0 .or. read_real .lt. 0.0_dp .or. &
620 (has_minutes .and. read_real .ge. 60.0_dp)) then
621 call set_error_or_throw( &
622 'Error parsing duration: Invalid seconds value', ierr)
623 return
624 end if
625
626 parsed_seconds = parsed_seconds + read_real
627 runtime_seconds = parsed_seconds
628
629 if (allocated(time_string)) deallocate(time_string)
630 end function read_duration_internal
631
633 subroutine set_error_or_throw(message, ierr)
634 character(len=*), intent(in) :: message
635 integer, optional, intent(out) :: ierr
636
637 if (present(ierr)) then
638 ierr = 1
639 else
640 call neko_error(trim(message))
641 end if
642 end subroutine set_error_or_throw
643end module utils
__inline__ __device__ void nonlinear_index(const int idx, const int lx, int *index)
Definition bc_utils.h:44
double real
integer, parameter, public dp
Definition num_types.f90:10
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:14
Utilities.
Definition utils.f90:35
integer function, public extract_fld_file_index(fld_filename, default_index)
Extracts the index of a field file. For example, "myfield.f00045" will return 45. If the suffix of th...
Definition utils.f90:209
character(:) function, allocatable, public concat_string_array(array, sep, prepend)
Concatenate an array of strings into one string with array items separated by spaces.
Definition utils.f90:424
subroutine, public neko_type_registration_error(base_type, wrong_type, known)
Definition utils.f90:393
subroutine, public filename_name(fname, name)
Extract the base name of a file (without path and suffix)
Definition utils.f90:103
subroutine set_error_or_throw(message, ierr)
Raise parser error or set ierr, depending on call mode.
Definition utils.f90:634
character(len=100) function, dimension(:), allocatable, public split_string(string, delimiter)
Split a string based on delimiter (tokenizer) OBS: very hacky, this should really be improved,...
Definition utils.f90:251
pure logical function, public index_is_on_facet(i, j, k, lx, ly, lz, facet)
Definition utils.f90:314
subroutine neko_error_msg(error_msg)
Reports an error and stops execution.
Definition utils.f90:359
pure integer function, public linear_index(i, j, k, l, lx, ly, lz)
Compute the address of a (i,j,k,l) array with sizes (1:lx, 1:ly, 1:lz, :)
Definition utils.f90:289
subroutine read_duration_scalar(runtime_string, runtime_seconds, ierr)
Parse runtime string to total seconds. Supported formats are:
Definition utils.f90:449
integer, parameter, public neko_fname_len
Definition utils.f90:42
subroutine, public filename_split(fname, path, name, suffix)
Extract file name components.
Definition utils.f90:131
subroutine, public neko_warning(warning_msg)
Reports a warning to standard output.
Definition utils.f90:413
real(kind=dp) function read_duration_internal(runtime_string, ierr)
Parse runtime string to total seconds.
Definition utils.f90:544
subroutine, public filename_chsuffix(fname, new_fname, new_suffix)
Change a filename's suffix.
Definition utils.f90:156
subroutine read_duration_components(runtime_string, runtime_values, ierr)
Parse runtime string to components. The output array maps to the largest unit available based on size...
Definition utils.f90:473
subroutine, public filename_suffix(fname, suffix)
Extract a filename's suffix.
Definition utils.f90:124
subroutine, public neko_type_error(base_type, wrong_type, known_types)
Reports an error allocating a type for a particular base pointer class.
Definition utils.f90:376
recursive subroutine, public mkdir(path, mode)
Recursively create a directory and all parent directories if they do not exist. This should be safer ...
Definition utils.f90:174
integer, parameter, public neko_varname_len
Definition utils.f90:43
pure integer function, public filename_tslash_pos(fname)
Find position (in the string) of a filename's trailing slash.
Definition utils.f90:81
subroutine neko_error_plain(error_code)
Reports an error and stops execution.
Definition utils.f90:338
pure integer function, public filename_suffix_pos(fname)
Find position (in the string) of a filename's suffix.
Definition utils.f90:74
subroutine, public filename_path(fname, path)
Extract the path to a file.
Definition utils.f90:88