Neko 1.99.3
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 if (present(error_code)) then
341 write(error_unit, *) '*** ERROR ***', error_code
342 error stop
343 else
344 write(error_unit, *) '*** ERROR ***'
345 error stop
346 end if
347
348 end subroutine neko_error_plain
349
352 subroutine neko_error_msg(error_msg)
353 character(len=*) :: error_msg
354 write(error_unit, *) '*** ERROR: ', trim(error_msg), ' ***'
355 error stop
356 end subroutine neko_error_msg
357
364 subroutine neko_type_error(base_type, wrong_type, known_types)
365 character(len=*), intent(in) :: base_type
366 character(len=*), intent(in) :: wrong_type
367 character(len=*), intent(in) :: known_types(:)
368 integer :: i
369
370 write(error_unit, *) '*** ERROR WHEN SELECTING TYPE ***'
371 write(error_unit, *) 'Type ', wrong_type, ' does not exist for ', base_type
372 write(error_unit, *) 'Valid types are:'
373 do i = 1, size(known_types)
374 write(error_unit, *) " ", known_types(i)
375 end do
376 error stop
377 end subroutine neko_type_error
378
379 subroutine neko_type_registration_error(base_type, wrong_type, known)
380 character(len=*), intent(in) :: base_type
381 character(len=*), intent(in) :: wrong_type
382 logical, intent(in) :: known
383
384 write(error_unit, *) '*** ERROR WHEN REGISTERING TYPE ***'
385 write(error_unit, *) 'Type name ', wrong_type, &
386 ' conflicts with and already existing ', base_type, " type"
387 if (known) then
388 write(error_unit, *) 'Please rename your custom type.'
389 else
390 write(error_unit, *) 'The already existing type is also custom.' // &
391 ' Make all custom type names unique!'
392 end if
393 error stop
394 end subroutine neko_type_registration_error
395
397 subroutine neko_warning(warning_msg)
398 character(len=*) :: warning_msg
399 write(output_unit, *) '*** WARNING: ', trim(warning_msg), ' ***'
400 end subroutine neko_warning
401
407 function concat_string_array(array, sep, prepend) result(result)
408 character(len=*), intent(in) :: array(:)
409 character(len=*), intent(in) :: sep
410 logical, intent(in) :: prepend
411 character(:), allocatable :: result
412 integer :: i
413
414 result = trim(array(1))
415 do i = 2, size(array)
416 result = result // sep // trim(array(i))
417 end do
418
419 if (prepend) then
420 result = sep // result
421 end if
422
423 end function concat_string_array
424
431 subroutine read_duration_scalar(runtime_string, runtime_seconds, &
432 ierr)
433 character(len=*), intent(in) :: runtime_string
434 real(kind=rp), intent(inout) :: runtime_seconds
435 integer, optional, intent(out) :: ierr
436 real(kind=dp) :: parsed_seconds
437
438 if (present(ierr)) ierr = 0
439 if (len_trim(runtime_string) .eq. 0) return
440
441 parsed_seconds = read_duration_internal(runtime_string, ierr)
442
443 if (present(ierr)) then
444 if (ierr .ne. 0) return
445 end if
446
447 runtime_seconds = parsed_seconds
448 end subroutine read_duration_scalar
449
456 subroutine read_duration_components(runtime_string, runtime_values, ierr)
457 character(len=*), intent(in) :: runtime_string
458 real(kind=rp), intent(inout) :: runtime_values(:)
459 integer, optional, intent(out) :: ierr
460 integer, parameter :: i64 = selected_int_kind(18)
461 integer :: n_values
462 integer(kind=i64) :: total_whole, days, hours, minutes, seconds_whole
463 real(kind=dp) :: parsed_seconds, second_value, frac_seconds
464
465 if (present(ierr)) ierr = 0
466 if (len_trim(runtime_string) .eq. 0) return
467
468 n_values = size(runtime_values)
469 if (n_values .lt. 1 .or. n_values .gt. 4) then
470 call set_error_or_throw( &
471 'Error parsing duration: output array size must be 1 to 4', ierr)
472 return
473 end if
474
475 parsed_seconds = read_duration_internal(runtime_string, ierr)
476
477 if (present(ierr)) then
478 if (ierr .ne. 0) return
479 end if
480
481 total_whole = int(parsed_seconds, kind=i64)
482 frac_seconds = parsed_seconds - real(total_whole, kind=dp)
483 if (frac_seconds .lt. 0.0_dp) frac_seconds = 0.0_dp
484
485 days = total_whole / 86400_i64
486 total_whole = total_whole - 86400_i64 * days
487 hours = total_whole / 3600_i64
488 total_whole = total_whole - 3600_i64 * hours
489 minutes = total_whole / 60_i64
490 seconds_whole = total_whole - 60_i64 * minutes
491
492 second_value = real(seconds_whole, kind=dp) + frac_seconds
493 if (second_value .ge. 60.0_dp) then
494 second_value = second_value - 60.0_dp
495 minutes = minutes + 1_i64
496 if (minutes .ge. 60_i64) then
497 minutes = minutes - 60_i64
498 hours = hours + 1_i64
499 if (hours .ge. 24_i64) then
500 hours = hours - 24_i64
501 days = days + 1_i64
502 end if
503 end if
504 end if
505
506 select case (n_values)
507 case (4)
508 runtime_values(1) = real(days, kind=rp)
509 runtime_values(2) = real(hours, kind=rp)
510 runtime_values(3) = real(minutes, kind=rp)
511 runtime_values(4) = real(second_value, kind=rp)
512 case (3)
513 runtime_values(1) = real(days * 24_i64 + hours, kind=rp)
514 runtime_values(2) = real(minutes, kind=rp)
515 runtime_values(3) = real(second_value, kind=rp)
516 case (2)
517 runtime_values(1) = real((days * 24_i64 + hours) * 60_i64 + &
518 minutes, kind=rp)
519 runtime_values(2) = real(second_value, kind=rp)
520 case (1)
521 runtime_values(1) = real(parsed_seconds, kind=rp)
522 end select
523 end subroutine read_duration_components
524
526 real(kind=dp) function read_duration_internal(runtime_string, ierr) &
527 result(runtime_seconds)
528 character(len=*), intent(in) :: runtime_string
529 integer, optional, intent(out) :: ierr
530 character(len=:), allocatable :: time_string
531 real(kind=dp) :: parsed_seconds, read_real
532 logical :: has_minutes, has_hours, has_days
533 integer :: read_int, ios, sep
534
535 if (present(ierr)) ierr = 0
536
537 runtime_seconds = 0.0_dp
538 time_string = trim(adjustl(runtime_string))
539
540 sep = index(time_string, ':')
541 has_minutes = sep .gt. 0
542 has_hours = .false.
543 if (has_minutes .and. sep .lt. len_trim(time_string)) then
544 has_hours = index(time_string(sep + 1:len_trim(time_string)), ':') &
545 .gt. 0
546 end if
547 has_days = index(time_string, '-') .gt. 0
548
549 if ((has_days .and. .not. has_hours) .or. &
550 (has_hours .and. .not. has_minutes)) then
551 call set_error_or_throw('Error parsing duration: Bad format', ierr)
552 return
553 end if
554
555 parsed_seconds = 0.0_dp
556
557 ! Read the days field.
558 if (has_days) then
559 sep = index(time_string, '-')
560 read(time_string(1:sep - 1), *, iostat=ios) read_int
561 if (ios .ne. 0 .or. read_int .lt. 0) then
562 call set_error_or_throw( &
563 'Error parsing duration: Invalid days value', ierr)
564 return
565 end if
566
567 time_string = time_string(sep + 1:)
568 parsed_seconds = parsed_seconds + real(86400 * read_int, kind=dp)
569 end if
570
571 ! Read the hours.
572 if (has_hours) then
573 sep = index(time_string, ':')
574 read(time_string(1:sep - 1), *, iostat=ios) read_int
575 if (ios .ne. 0 .or. read_int .lt. 0 .or. &
576 (has_days .and. read_int .gt. 23)) then
577 call set_error_or_throw( &
578 'Error parsing duration: Invalid hours value', ierr)
579 return
580 end if
581
582 time_string = time_string(sep + 1:)
583 parsed_seconds = parsed_seconds + real(3600 * read_int, kind=dp)
584 end if
585
586 ! Read the minutes.
587 if (has_minutes) then
588 sep = index(time_string, ':')
589 read(time_string(1:sep - 1), *, iostat=ios) read_int
590 if (ios .ne. 0 .or. read_int .lt. 0 .or. &
591 (has_hours .and. read_int .gt. 59)) then
592 call set_error_or_throw( &
593 'Error parsing duration: Invalid minutes value', ierr)
594 return
595 end if
596
597 time_string = time_string(sep + 1:)
598 parsed_seconds = parsed_seconds + real(60 * read_int, kind=dp)
599 end if
600
601 ! Read the seconds.
602 read(time_string, *, iostat=ios) read_real
603 if (ios .ne. 0 .or. read_real .lt. 0.0_dp .or. &
604 (has_minutes .and. read_real .ge. 60.0_dp)) then
605 call set_error_or_throw( &
606 'Error parsing duration: Invalid seconds value', ierr)
607 return
608 end if
609
610 parsed_seconds = parsed_seconds + read_real
611 runtime_seconds = parsed_seconds
612
613 if (allocated(time_string)) deallocate(time_string)
614 end function read_duration_internal
615
617 subroutine set_error_or_throw(message, ierr)
618 character(len=*), intent(in) :: message
619 integer, optional, intent(out) :: ierr
620
621 if (present(ierr)) then
622 ierr = 1
623 else
624 call neko_error(trim(message))
625 end if
626 end subroutine set_error_or_throw
627end 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:9
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:12
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:408
subroutine, public neko_type_registration_error(base_type, wrong_type, known)
Definition utils.f90:380
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:618
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:353
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:433
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:398
real(kind=dp) function read_duration_internal(runtime_string, ierr)
Parse runtime string to total seconds.
Definition utils.f90:528
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:457
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:365
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