From 7063c59b6ae9ead047fae155f941c7b5f872b9a5 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 9 Dec 2023 14:22:14 +0100 Subject: [PATCH 1/6] consistent capitalization --- src/quit.f90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/quit.f90 b/src/quit.f90 index 4395aa24d..288982363 100644 --- a/src/quit.f90 +++ b/src/quit.f90 @@ -27,10 +27,10 @@ subroutine quit(stop_id) PetscErrorCode :: err_PETSc - call h5open_f(err_HDF5) ! prevents error if not opened yet - if (err_HDF5 < 0) write(ERROR_UNIT,'(a,i5)') ' Error in h5open_f ',err_HDF5 - call h5close_f(err_HDF5) - if (err_HDF5 < 0) write(ERROR_UNIT,'(a,i5)') ' Error in h5close_f ',err_HDF5 + call H5Open_f(err_HDF5) ! prevents error if not opened yet + if (err_HDF5 < 0) write(ERROR_UNIT,'(a,i5)') ' Error in H5Open_f ',err_HDF5 + call H5Close_f(err_HDF5) + if (err_HDF5 < 0) write(ERROR_UNIT,'(a,i5)') ' Error in H5Close_f ',err_HDF5 call PetscFinalize(err_PETSc) From 5d07851d6052f19dc07dd7863366f834fce476ee Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 9 Dec 2023 14:40:58 +0100 Subject: [PATCH 2/6] use stdout but do nothing if redirected --- python/damask/util.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/python/damask/util.py b/python/damask/util.py index 513cab87f..a32641708 100644 --- a/python/damask/util.py +++ b/python/damask/util.py @@ -930,8 +930,8 @@ class ProgressBar: self.time_start = self.time_last_update = _datetime.datetime.now() self.fraction_last = 0.0 - _sys.stderr.write(f"{self.prefix} {'░'*self.bar_length} 0% ETA n/a") - _sys.stderr.flush() + if _sys.stdout.isatty(): + _sys.stdout.write(f"{self.prefix} {'░'*self.bar_length} 0% ETA n/a") def update(self, iteration: int) -> None: @@ -944,12 +944,11 @@ class ProgressBar: bar = '█' * filled_length + '░' * (self.bar_length - filled_length) remaining_time = (_datetime.datetime.now() - self.time_start) \ * (self.total - (iteration+1)) / (iteration+1) - remaining_time -= _datetime.timedelta(microseconds=remaining_time.microseconds) # remove μs - _sys.stderr.write(f'\r{self.prefix} {bar} {fraction:>4.0%} ETA {remaining_time}') - _sys.stderr.flush() + remaining_time -= _datetime.timedelta(microseconds=remaining_time.microseconds) # remove μs + if _sys.stdout.isatty(): + _sys.stdout.write(f'\r{self.prefix} {bar} {fraction:>4.0%} ETA {remaining_time}') self.fraction_last = fraction - if iteration == self.total - 1: - _sys.stderr.write('\n') - _sys.stderr.flush() + if iteration == self.total - 1 and _sys.stdout.isatty(): + _sys.stdout.write('\n') From 456b3fb76d2283f5a58808132858cb8bfbb05d1e Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 9 Dec 2023 15:17:52 +0100 Subject: [PATCH 3/6] functionality to check whether STDERR/STDOUT are redirected --- src/C_routines.c | 10 ++++++++++ src/system_routines.f90 | 40 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/C_routines.c b/src/C_routines.c index 26d704974..0f5738cfc 100644 --- a/src/C_routines.c +++ b/src/C_routines.c @@ -85,6 +85,16 @@ void inflate_c(const uLong *s_deflated, const uLong *s_inflated, const Byte defl } } + +int stdout_isatty_c(){ + return isatty(STDOUT_FILENO); +} + +int stderr_isatty_c(){ + return isatty(STDERR_FILENO); +} + + #ifdef FYAML void to_flow_c(char **flow, long* length_flow, const char *mixed){ struct fy_document *fyd = NULL; diff --git a/src/system_routines.f90 b/src/system_routines.f90 index aa4a140d6..b15485f1c 100644 --- a/src/system_routines.f90 +++ b/src/system_routines.f90 @@ -4,9 +4,9 @@ !-------------------------------------------------------------------------------------------------- module system_routines use, intrinsic :: ISO_C_Binding + use, intrinsic :: ISO_fortran_env use prec - use IO implicit none(type,external) private @@ -21,6 +21,8 @@ module system_routines signalint_C, & signalusr1_C, & signalusr2_C, & + STDOUT_isatty, & + STDERR_isatty, & f_c_string, & free_C @@ -91,6 +93,20 @@ module system_routines type(C_PTR), value :: ptr end subroutine free_C + function stdout_isatty_C() bind(C) + use, intrinsic :: ISO_C_Binding, only: C_INT + implicit none(type,external) + + integer(C_INT) :: stdout_isatty_C + end function stdout_isatty_C + + function stderr_isatty_C() bind(C) + use, intrinsic :: ISO_C_Binding, only: C_INT + implicit none(type,external) + + integer(C_INT) :: stderr_isatty_C + end function stderr_isatty_C + end interface contains @@ -101,7 +117,7 @@ contains !-------------------------------------------------------------------------------------------------- subroutine system_routines_init() - print'(/,1x,a)', '<<<+- system_routines init -+>>>'; flush(IO_STDOUT) + print'(/,1x,a)', '<<<+- system_routines init -+>>>'; flush(OUTPUT_UNIT) call system_routines_selfTest() @@ -229,6 +245,26 @@ pure function f_c_string(f_string) result(c_string) end function f_c_string +!-------------------------------------------------------------------------------------------------- +!> @brief +!-------------------------------------------------------------------------------------------------- +logical function STDOUT_isatty() + + STDOUT_isatty = merge(.true.,.false.,stdout_isatty_C()==1) + +end function STDOUT_isatty + + +!-------------------------------------------------------------------------------------------------- +!> @brief +!-------------------------------------------------------------------------------------------------- +logical function STDERR_isatty() + + STDERR_isatty = merge(.true.,.false.,stderr_isatty_C()==1) + +end function STDERR_isatty + + !-------------------------------------------------------------------------------------------------- !> @brief Check correctness of some system_routine functions. !-------------------------------------------------------------------------------------------------- From 65c0a2d5ca3dc41993821c87eb960c3af422b3d9 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 9 Dec 2023 15:19:27 +0100 Subject: [PATCH 4/6] more logical split --- src/system_routines.f90 | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/system_routines.f90 b/src/system_routines.f90 index b15485f1c..3068f4aa5 100644 --- a/src/system_routines.f90 +++ b/src/system_routines.f90 @@ -32,8 +32,8 @@ module system_routines function setCWD_C(cwd) bind(C) use, intrinsic :: ISO_C_Binding, only: C_INT, C_CHAR - implicit none(type,external) + implicit none(type,external) integer(C_INT) :: setCWD_C character(kind=C_CHAR), dimension(*), intent(in) :: cwd end function setCWD_C @@ -41,8 +41,8 @@ module system_routines subroutine getCWD_C(cwd, stat) bind(C) use, intrinsic :: ISO_C_Binding, only: C_INT, C_CHAR use prec - implicit none(type,external) + implicit none(type,external) character(kind=C_CHAR), dimension(pPathLen+1), intent(out) :: cwd ! NULL-terminated array integer(C_INT), intent(out) :: stat end subroutine getCWD_C @@ -50,8 +50,8 @@ module system_routines subroutine getHostName_C(hostname, stat) bind(C) use, intrinsic :: ISO_C_Binding, only: C_INT, C_CHAR use prec - implicit none(type,external) + implicit none(type,external) character(kind=C_CHAR), dimension(pSTRLEN+1), intent(out) :: hostname ! NULL-terminated array integer(C_INT), intent(out) :: stat end subroutine getHostName_C @@ -59,51 +59,51 @@ module system_routines subroutine getUserName_C(username, stat) bind(C) use, intrinsic :: ISO_C_Binding, only: C_INT, C_CHAR use prec - implicit none(type,external) + implicit none(type,external) character(kind=C_CHAR), dimension(pSTRLEN+1), intent(out) :: username ! NULL-terminated array integer(C_INT), intent(out) :: stat end subroutine getUserName_C subroutine signalint_C(handler) bind(C) use, intrinsic :: ISO_C_Binding, only: C_FUNPTR - implicit none(type,external) + implicit none(type,external) type(C_FUNPTR), intent(in), value :: handler end subroutine signalint_C subroutine signalusr1_C(handler) bind(C) use, intrinsic :: ISO_C_Binding, only: C_FUNPTR - implicit none(type,external) + implicit none(type,external) type(C_FUNPTR), intent(in), value :: handler end subroutine signalusr1_C subroutine signalusr2_C(handler) bind(C) use, intrinsic :: ISO_C_Binding, only: C_FUNPTR - implicit none(type,external) + implicit none(type,external) type(C_FUNPTR), intent(in), value :: handler end subroutine signalusr2_C subroutine free_C(ptr) bind(C,name='free') use, intrinsic :: ISO_C_Binding, only: C_PTR - implicit none(type,external) + implicit none(type,external) type(C_PTR), value :: ptr end subroutine free_C function stdout_isatty_C() bind(C) use, intrinsic :: ISO_C_Binding, only: C_INT - implicit none(type,external) + implicit none(type,external) integer(C_INT) :: stdout_isatty_C end function stdout_isatty_C function stderr_isatty_C() bind(C) use, intrinsic :: ISO_C_Binding, only: C_INT - implicit none(type,external) + implicit none(type,external) integer(C_INT) :: stderr_isatty_C end function stderr_isatty_C From f3e7f51bb41cbb7cdf6ab150a4f41bf4b6daf1fa Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 9 Dec 2023 16:34:10 +0100 Subject: [PATCH 5/6] centralized functionality for handling of colors --- python/damask/util.py | 2 +- src/CLI.f90 | 6 +++--- src/IO.f90 | 49 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/python/damask/util.py b/python/damask/util.py index a32641708..eda668564 100644 --- a/python/damask/util.py +++ b/python/damask/util.py @@ -946,7 +946,7 @@ class ProgressBar: * (self.total - (iteration+1)) / (iteration+1) remaining_time -= _datetime.timedelta(microseconds=remaining_time.microseconds) # remove μs if _sys.stdout.isatty(): - _sys.stdout.write(f'\r{self.prefix} {bar} {fraction:>4.0%} ETA {remaining_time}') + _sys.stdout.write(f'\r{self.prefix} {bar} {fraction:>4.0%} ETA {remaining_time}') self.fraction_last = fraction diff --git a/src/CLI.f90 b/src/CLI.f90 index 323019815..0cae046f5 100644 --- a/src/CLI.f90 +++ b/src/CLI.f90 @@ -71,10 +71,10 @@ subroutine CLI_init() ! http://patorjk.com/software/taag/#p=display&f=Lean&t=DAMASK%203 #ifdef DEBUG - print'(a)', achar(27)//'[31m' + print'(a)', IO_color([255,0,0]) print'(1x,a,/)', 'debug version - debug version - debug version - debug version - debug version' #else - print'(a)', achar(27)//'[1;94m' + print'(a)', IO_color([67,128,208]) #endif print'(1x,a)', ' _/_/_/ _/_/ _/ _/ _/_/ _/_/_/ _/ _/ _/_/_/' print'(1x,a)', ' _/ _/ _/ _/ _/_/ _/_/ _/ _/ _/ _/ _/ _/' @@ -89,7 +89,7 @@ subroutine CLI_init() #ifdef DEBUG print'(/,1x,a)', 'debug version - debug version - debug version - debug version - debug version' #endif - print'(a)', achar(27)//'[0m' + print'(a)', IO_color() print'(1x,a)', 'F. Roters et al., Computational Materials Science 158:420–478, 2019' print'(1x,a)', 'https://doi.org/10.1016/j.commatsci.2018.04.030' diff --git a/src/IO.f90 b/src/IO.f90 index 71d51f697..ccef2fa69 100644 --- a/src/IO.f90 +++ b/src/IO.f90 @@ -13,8 +13,11 @@ module IO use prec use constants use misc +#ifndef MARC4DAMASK + use system_routines +#endif - implicit none(type,external) +implicit none(type,external) private character(len=*), parameter, public :: & @@ -42,6 +45,7 @@ module IO IO_strAsInt, & IO_strAsReal, & IO_strAsBool, & + IO_color, & IO_error, & IO_warning, & IO_STDOUT @@ -433,6 +437,41 @@ logical function IO_strAsBool(str) end function IO_strAsBool +!-------------------------------------------------------------------------------------------------- +!> @brief Set foreground and/or background color. +!> @details Only active if unit is a TTY. Does nothing for MSC.Marc or when writing to log file. +!> @details https://stackoverflow.com/questions/4842424 +!-------------------------------------------------------------------------------------------------- +function IO_color(fg,bg,unit) + + character(len=:), allocatable :: IO_color + integer, intent(in), dimension(3), optional :: fg, bg + integer, intent(in), optional :: unit !< output unit (default STDOUT) + + integer :: unit_ + + + IO_color = '' + +#if !(defined(MARC4DAMASK) || defined(LOGFILE)) + unit_ = misc_optional(unit,IO_STDOUT) + if (unit_ == IO_STDOUT .and. .not. STDOUT_isatty()) return + if (unit_ == IO_STDERR .and. .not. STDERR_isatty()) return + + if (present(fg)) & + IO_color = IO_color//achar(27)//'[38;2;'//IO_intAsStr(fg(1))//';' & + //IO_intAsStr(fg(2))//';' & + //IO_intAsStr(fg(3))//'m' + if (present(bg)) & + IO_color = IO_color//achar(27)//'[48;2;'//IO_intAsStr(bg(1))//';' & + //IO_intAsStr(bg(2))//';' & + //IO_intAsStr(bg(3))//'m' + + if (.not. present(fg) .and. .not. present(bg)) IO_color = achar(27)//'[0m' +#endif + +end function IO_color + !-------------------------------------------------------------------------------------------------- !> @brief Write error statements and terminate the run with exit #9xxx. @@ -722,8 +761,8 @@ subroutine panel(paneltype,ID,msg,ext_msg,label1,ID1,label2,ID2) character(len=*), parameter :: DIVIDER = repeat('─',panelwidth) - if (.not. present(label1) .and. present(ID1)) error stop 'missing label for value 1' - if (.not. present(label2) .and. present(ID2)) error stop 'missing label for value 2' + if (.not. present(label1) .and. present(ID1)) error stop 'missing label for value 1' + if (.not. present(label2) .and. present(ID2)) error stop 'missing label for value 2' ID_ = IO_intAsStr(ID) if (present(label1)) msg1 = label1 @@ -731,8 +770,8 @@ subroutine panel(paneltype,ID,msg,ext_msg,label1,ID1,label2,ID2) if (present(ID1)) msg1 = msg1//' '//IO_intAsStr(ID1) if (present(ID2)) msg2 = msg2//' '//IO_intAsStr(ID2) - if (paneltype == 'error') msg_ = achar(27)//'[31m'//trim(msg)//achar(27)//'[0m' - if (paneltype == 'warning') msg_ = achar(27)//'[33m'//trim(msg)//achar(27)//'[0m' + if (paneltype == 'error') msg_ = IO_color([255,0,0], unit=IO_STDERR)//trim(msg)//IO_color(unit=IO_STDERR) + if (paneltype == 'warning') msg_ = IO_color([255,255,0],unit=IO_STDERR)//trim(msg)//IO_color(unit=IO_STDERR) !$OMP CRITICAL (write2out) write(IO_STDERR,'(/,a)') ' ┌'//DIVIDER//'┐' write(formatString,'(a,i2,a)') '(a,24x,a,1x,i0,',max(1,panelwidth-24-len_trim(paneltype)-1-len_trim(ID_)),'x,a)' From 4320e9847d84ff665e5c621ea820691cf1c9418b Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 10 Dec 2023 06:52:06 +0100 Subject: [PATCH 6/6] more systematic handling of colored output isatty for Fortran needs to translate the units/file descriptors --- src/C_routines.c | 4 ++++ src/IO.f90 | 16 +++++++--------- src/system_routines.f90 | 40 ++++++++++++++++++++++++++-------------- 3 files changed, 37 insertions(+), 23 deletions(-) diff --git a/src/C_routines.c b/src/C_routines.c index 0f5738cfc..5886a7b81 100644 --- a/src/C_routines.c +++ b/src/C_routines.c @@ -94,6 +94,10 @@ int stderr_isatty_c(){ return isatty(STDERR_FILENO); } +int stdin_isatty_c(){ + return isatty(STDIN_FILENO); +} + #ifdef FYAML void to_flow_c(char **flow, long* length_flow, const char *mixed){ diff --git a/src/IO.f90 b/src/IO.f90 index ccef2fa69..88d747ce0 100644 --- a/src/IO.f90 +++ b/src/IO.f90 @@ -438,25 +438,23 @@ end function IO_strAsBool !-------------------------------------------------------------------------------------------------- -!> @brief Set foreground and/or background color. -!> @details Only active if unit is a TTY. Does nothing for MSC.Marc or when writing to log file. +!> @brief Return string to set foreground and/or background color. +!> @details Only active if unit is a TTY. Does nothing for MSC.Marc. No color disables formatting. !> @details https://stackoverflow.com/questions/4842424 !-------------------------------------------------------------------------------------------------- function IO_color(fg,bg,unit) character(len=:), allocatable :: IO_color - integer, intent(in), dimension(3), optional :: fg, bg + integer, intent(in), dimension(3), optional :: & + fg, & !< foreground color (8 bit RGB) + bg !< background color (8 bit RGB) integer, intent(in), optional :: unit !< output unit (default STDOUT) - integer :: unit_ - IO_color = '' -#if !(defined(MARC4DAMASK) || defined(LOGFILE)) - unit_ = misc_optional(unit,IO_STDOUT) - if (unit_ == IO_STDOUT .and. .not. STDOUT_isatty()) return - if (unit_ == IO_STDERR .and. .not. STDERR_isatty()) return +#ifndef MARC4DAMASK + if (.not. isatty(misc_optional(unit,IO_STDOUT))) return if (present(fg)) & IO_color = IO_color//achar(27)//'[38;2;'//IO_intAsStr(fg(1))//';' & diff --git a/src/system_routines.f90 b/src/system_routines.f90 index 3068f4aa5..81609da63 100644 --- a/src/system_routines.f90 +++ b/src/system_routines.f90 @@ -21,8 +21,7 @@ module system_routines signalint_C, & signalusr1_C, & signalusr2_C, & - STDOUT_isatty, & - STDERR_isatty, & + isatty, & f_c_string, & free_C @@ -107,6 +106,14 @@ module system_routines integer(C_INT) :: stderr_isatty_C end function stderr_isatty_C + function stdin_isatty_C() bind(C) + use, intrinsic :: ISO_C_Binding, only: C_INT + + implicit none(type,external) + integer(C_INT) :: stdin_isatty_C + end function stdin_isatty_C + + end interface contains @@ -246,23 +253,28 @@ end function f_c_string !-------------------------------------------------------------------------------------------------- -!> @brief +!> @brief Test whether a file descriptor refers to a terminal. +!> @detail A terminal is neither a file nor a redirected STDOUT/STDERR/STDIN. !-------------------------------------------------------------------------------------------------- -logical function STDOUT_isatty() +logical function isatty(unit) - STDOUT_isatty = merge(.true.,.false.,stdout_isatty_C()==1) - -end function STDOUT_isatty + integer, intent(in) :: unit -!-------------------------------------------------------------------------------------------------- -!> @brief -!-------------------------------------------------------------------------------------------------- -logical function STDERR_isatty() + select case(unit) +#ifndef LOGFILE + case (OUTPUT_UNIT) + isatty = stdout_isatty_C()==1 + case (ERROR_UNIT) + isatty = stderr_isatty_C()==1 +#endif + case (INPUT_UNIT) + isatty = stdin_isatty_C()==1 + case default + isatty = .false. + end select - STDERR_isatty = merge(.true.,.false.,stderr_isatty_C()==1) - -end function STDERR_isatty +end function isatty !--------------------------------------------------------------------------------------------------