#!/bin/bash
#---------------------------------*- sh -*-------------------------------------
# ==  == ====== ====   ====    |
#                   \\     ||  | Multiphase Code Repository by HZDR
# ======   //   ||  || ===//   | Website: https://doi.org/10.14278/rodare.767
# ||  ||  //    ||  // || \\   | License: GPL-3.0-or-later
# ==  == ====== ====   ==  ==  |
#------------------------------------------------------------------------------
# License
#     This file is part of the Multiphase Code Repository by HZDR.
#
#     Copyright (C) 2025 by Helmholtz-Zentrum Dresden-Rossendorf e.V. (HZDR),
#     Website: https://hzdr.de
#
#     Multiphase Code Repository by HZDR is based on the free software for
#     computational fluid dynamics (CFD) from the OpenFOAM Foundation.
#     Copyright (C) 2025 by OpenFOAM Foundation, Website: https://openfoam.org
#
#     If you are interested in which files are original OpenFOAM Foundation
#     files, which OpenFOAM Foundation files were modified, and which files were
#     newly created, see FILES.md.
#
#     Multiphase Code Repository by HZDR is free software: you can redistribute
#     it and/or modify it under the terms of the GNU General Public License as
#     published by the Free Software Foundation, either version 3 of the
#     License, or (at your option) any later version.
#
#     Multiphase Code Repository by HZDR is distributed in the hope that it will
#     be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
#     Public License for more details.
#
#     You should have received a copy of the GNU General Public License along
#     with the Multiphase Code Repository by HZDR. If not, see
#     <http://www.gnu.org/licenses/>.
#
# Description
#     Script is meant to be launched in parallel to an OpenFOAM solver and shows
#     simulation progress in terms of simulated time. It runs as long as the
#     application, defined in system/controlDict, is running and subsequently
#     exits on its own. For usage change the Allrun script as follows:
#
#         runParallel $application
#
#     to
#
#         runParallel $application &
#         progressBar.sh; wait
#
#     The package "bc" is required (available for Ubuntu).
#
#------------------------------------------------------------------------------


PROGNAME=$(basename "$0")

usage() {
   while [ "$#" -ge 1 ]; do echo "$1"; shift; done
   cat<<USAGE

usage: $PROGNAME [OPTION]

options:
  -i  | --instant   print single instant and exit
  -r#               refresh rate in seconds
  -h  | --help      print this usage

USAGE
   exit 1
}

# script parameters
interval=1
barlength=25
instant=false

while [ "$#" -gt 0 ]
do
    case "$1" in
    -r[0-9]*)
        interval=${1//-r/}
        shift
        ;;
    -i | --instant)
        instant=true
        shift
        ;;
    -*)
        usage "Error: Invalid option '$1'"
        ;;
    *)
        break
        ;;
    esac
done

#------------------------------------------------------------------------------

# get times and names

if [ -f "system/controlDict" ]; then
    file="system/controlDict"
elif [ -f "system/controlDict.orig" ]; then
    file="system/controlDict.orig"
else
    echo "controlDict(.orig) not found"
    exit
fi

application=$(sed -ne 's/^ *application\s*\([a-zA-Z]*\)\s*;.*$/\1/p' $file)
if [ -z "${application}" ]; then
    solver=$(grep 'solver' $file)
    regionSolvers=$(grep 'regionSolvers' $file)
    if [ -n "${solver}" ]; then
        application="foamRun"
    elif [ -n "${regionSolvers}" ]; then
        application="foamMultiRun"
    else
        echo "No solvers found. Assuming foamRun application."
        application="foamRun"
    fi
fi

#------------------------------------------------------------------------------

# functions

# sleep without wasting cpu time
sleepNoLoad() { read -rt "$1" <&1 3<&- 3<&0 <&3; }

printHMS() { printf '%02d:%02d:%02d' $(($1/3600)) $(($1%3600/60)) $(($1%60)); }

printDHMS() { printf '%d-%02d:%02d:%02d' $(($1/86400)) $(($1%86400/3600)) \
    $(($1%3600/60)) $(($1%60)); }

printTime() {
    if [[ "$1" -le "86400" ]]; then
        printHMS "$1"
    else
        printDHMS "$1";
    fi
}

printRunning() {
    # get endTime
    endTime=$(sed -ne 's/^ *endTime\s*\([0-9.e-]*\)\s*;.*$/\1/p' $file)
    endTime=$(printf '%f' "${endTime}")

    # get the actual time
    runTime=$(  tail -n 5000 "log.$application" | \
                grep "^\ *Time = " | \
                tail -1 | \
                sed -ne 's/^\ *Time = \([0-9.e-]*\)s$/\1/p')
    runTime=$(printf '%f' "${runTime}")

    # get execution time
    execTime=$( tail -n 5000 "log.$application" | \
                grep "^\ *ExecutionTime = " | \
                tail -1 | \
                sed -ne 's/^\ *ExecutionTime = \([0-9.]*\) s.*$/\1/p')
    execTime=$(printf '%.0f' "${execTime}")

    # calculate progress and number of bars
    prog=$(echo "($runTime - $startTime)/($endTime-$startTime)" | bc -l)
    bars=$(printf "%.$2f" "$(echo "$prog*$barlength" | bc -l)")

    if [[ $(echo "$runTime == $startTime" | bc -l) -eq 1  ]]; then
        remTime=0
    else
        remTime=$(printf '%.0f' "$(echo "$execTime*($endTime - \
            $runTime)/($runTime - $startTime)" | bc -l)")
    fi

    # print bar and numbers
    printf "["
    for ((done=0; done<bars; done++)); do printf "="; done
    for ((remain=bars; remain<barlength; remain++)); do printf " "; done
    printf "] "

    printf '%2.1f %%, ' "$(echo "($prog*100)" | bc -l)"
    printf 'remaining = '
    printTime "$remTime"
    printf ', '

    printf 't = %s s                                             ' "$runTime"
    echo -ne '\r'
}

printFinal() {
    # get the actual time
    runTime=$(  tail -n 5000 "log.$application" | \
                grep "^\ *Time = " | \
                tail -1 | \
                sed -ne 's/^\ *Time = \([0-9.e-]*\)s$/\1/p')
    runTime=$(printf '%f' "${runTime}")

    # get execution time
    execTime=$( tail -n 5000 "log.$application" | \
                grep "^\ *ExecutionTime = " | \
                tail -1 | \
                sed -ne 's/^\ *ExecutionTime = \([0-9.]*\) s.*$/\1/p')
    execTime=$(printf '%.0f' "${execTime}")

    # print bar and numbers
    printf '['
    for ((done=0; done<barlength; done++)); do printf "="; done
    printf '] '
    printf '100 %%, '
    printf 'computation time = '
    printTime "$execTime"
    printf ', '
    printf 't = %s s                                             \n' "$runTime"
}

#------------------------------------------------------------------------------

# wait for target application to run
if [ "$instant" == "false" ]; then
    sleepNoLoad 0.2
    while true; do
        if [ -f "log.$application" ]; then
            containsTime=$(tail -n 1000 "log.$application" | grep "^\ *Time = ")
            [[ -n "${containsTime}" ]] && break
        fi
        echo -ne 'Waiting for solver to run\r'
        sleepNoLoad "$interval"
    done
fi

# get actual starting time of simulation
startTime=$(    head -n 5000 "log.$application" | \
                grep "^\ *Time = " | \
                head -1 | \
                sed -ne 's/^\ *Time = \([0-9.e-]*\)s$/\1/p')
startTime=$(printf '%f' "${startTime}")

# print progress bar and keep up-to-date
if [ "$instant" == "true" ]; then
    printRunning "$@"
    echo ""
    exit 0
fi

PID=$(pidof "$application" | awk '{print $1}')
if [ -n "$PID" ]; then
    # loop while application is running

    while kill -0 "$PID" >/dev/null 2>&1; do
        printRunning "$@"
        sleepNoLoad "$interval" # wait for next execution
    done
    printFinal

else
    # loop until 'End' or 'FOAM FATAL ERROR' found,
    #   if application PID not available

    while true; do
        printRunning "$@"
        sleepNoLoad "$interval" # wait for next execution
        containsEnd=$(tail -n 10 "log.$application" | grep "End")
        containsError=$(tail -n 1000 "log.$application" | \
            grep "FOAM FATAL .*ERROR")
        if [ -n "${containsEnd}" ]; then
            printFinal
            break
        elif [ -n "${containsError}" ]; then
            echo
            echo "Found 'FOAM FATAL ERROR', aborting"
            break
        fi
    done
fi

#------------------------------------------------------------------------------
