#!/bin/sh
#---------------------------------*- sh -*-------------------------------------
# =========                 |
# \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
#  \\    /   O peration     |
#   \\  /    A nd           | Copyright (C) 1991-2010 OpenCFD Ltd.
#    \\/     M anipulation  |
#------------------------------------------------------------------------------
# License
#     This file is part of OpenFOAM.
#
#     OpenFOAM 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.
#
#     OpenFOAM 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 OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
#
# Script
#     foamLog
#
# Description
#     extracts info from log file
#
# Bugs
#     -solution singularity not handled
#------------------------------------------------------------------------------

PROGDIR=`dirname $0`
PROGNAME=`basename $0`

if [ -r $HOME/.${PROGNAME}.db ]; then
    DBFILE=$HOME/.${PROGNAME}.db
else
    DBFILE=$PROGDIR/$PROGNAME.db
fi


printUsage() {
cat <<LABUSAGE
$PROGNAME - extracts xy files from Foam logs.

Usage: $PROGNAME [-n][-s] <log>
           extracts xy files from log
       $PROGNAME -l <log>
           lists but does not extract
       $PROGNAME -h
           for a help message

LABUSAGE
}


printHelp() {
printUsage
cat <<LABHELP
The default is to extract for all the 'Solved for' variables the
initial residual, the final residual and the number of iterations. On
top of this a (user editable) database of standard non-solved for
variables is used to extract data like Courant number, execution time.

$PROGNAME -l shows all the possible variables but does not extract them.

The program will generate and run an awk script which writes a set of
files, logs/<var>_<subIter>, for every <var> specified, for every
occurrence inside a time step.

For variables that are 'Solved for' the initial residual name will 
be <var>, the final residual will get name <var>FinalRes,

The files are a simple xy format with the first column Time (default)
and the second the extracted values. Option -n creates single column
files with the extracted data only.


The query database is a simple text format with three entries per line, 
separated with '/'. Column 1 is the name of the variable (cannot contain
spaces), column 2 is the extended regular expression (egrep) to select
the line and column 3 is the string (fgrep) to select the column inside the
line. The value taken will be the first (non-space)word after this
column. The database will either be \$HOME/.${PROGNAME}.db or if not
found  $PROGDIR/${PROGNAME}.db.

Option -s suppresses the default information and only prints the extracted
variables.

LABHELP
}



myEcho() {
  if [ "$VERBOSE" ]; then
      echo "$*"
  fi
}

# getSolvedVars logFile
# Prints names of all 'solved for' variables in the log file.
getSolvedVars() {
    fgrep ' Solving for ' $1 | fgrep ',' | sed -e 's/.* Solving for \([^,]*\)[,:].*/\1/' | sort -u
}


# getQueries dbFile queryName
# Gets regular expressions for a certain queryName from the database
getQueries() {
    if [ ! -f "$1" ]; then
        echo "Cannot find dbFile $1"
        exit 1
    fi

    queryName=$2

    LINEQ=`grep -v '^#' $1 | awk -F '/' "/$queryName/ {if (\"$queryName\" "'!= $1) next; print $2}'`
    NUMQ=`grep -v '^#' $1 | awk -F '/' "/$queryName/ {if (\"$queryName\" "'!= $1) next; print $3}'`

    #echo "For $queryName found line selection /$LINEQ/ , column selection /$NUMQ/" 1>&2
    #if [ ! "$LINEQ" -o ! "$NUMQ" ]; then
    #    echo "Did not find query for $2 in database $1" 1>&2
    #fi
}


# getDbQueryList dbFile
# Echoes list of possible queries
getDbQueryList() {
    grep -v '^#' $1 | grep '[^ \t]' | awk -F '/' '{print $1}'
}


# getSolveQueryList logFile
# Echoes list of queries from "solved for" variables in log file
getSolveQueryList() {
    solvedVars=`getSolvedVars $1`

    for var in $solvedVars
    do
        echo "${var}"
        echo "${var}FinalRes"
        echo "${var}Iters"
    done
}

# getAllQueries dbFile logFile
# Gets all queries from database and from logfile
getAllQueries() {
    #-- All solved for queries from log file
    queries=`getSolveQueryList $2`

    #-- Add ones from database, present in log file
    # Note: just like awk, line selected with regular expression,
    #       column with string.
    dbQueries=`getDbQueryList $1`

    for var in $dbQueries
    do
        getQueries $1 "$var"
        line=`egrep "$LINEQ" $2`
        if [ "$line" ]; then
            column=`echo "$line" | fgrep "$NUMQ"`
            if [ "$column" ]; then
                queries="$queries $var"
            fi
        fi
    done

    for q in $queries
    do
        echo "$q"
    done | sort -u
}

#-----------------------------
# Main
#-----------------------------

# sort arguments
TIMENAME='Time'
VERBOSE='yes'
LISTONLY=''

while getopts nslh flags
do
    case $flags in
        n)  TIMENAME=""
            ;;
        h)  printHelp
            exit 0
            ;;
        s)  VERBOSE=""
            ;;
        l)  LISTONLY='yes'
            ;;
	    \?) printUsage
            exit 1
            ;;
    esac
done


# Shift options
shift `expr $OPTIND - 1`

if [ ! -f $DBFILE ]; then
    echo "$PROGNAME: Cannot read database $DBFILE"
    exit 1
fi

if [ "$LISTONLY" ]; then
    if [ $# -ne 1 ]; then
        printUsage
        exit 1
    fi
    LOG=$1;
    if [ ! -r $LOG ]; then
        echo "$PROGNAME: Cannot read log $LOG"
        exit 1
    fi
    getAllQueries $DBFILE $LOG
    exit 0
fi

if [ $# -ne 1 ]; then
    printUsage
    exit 1
fi

CASEDIR=.
LOG=$1
if [ ! -r $LOG ]; then
    echo "$PROGNAME: Cannot read log $LOG"
    exit 1
fi

QUERYNAMES=`getAllQueries $DBFILE $LOG`


if [ ! "$CASEDIR" ]; then
    printUsage
    exit 1
fi

if [ ! -d "$CASEDIR" ]; then
    echo "$PROGNAME: Cannot read $CASEDIR"
    exit 1
fi

if [ ! -f "$LOG" ]; then
    echo "$PROGNAME: Cannot read log file $LOG"
    exit 1
fi


#-- Make logs dir in case directory and put awk file there.

mkdir -p $CASEDIR/logs
AWKFILE=$CASEDIR/logs/$PROGNAME.awk

myEcho "Using:"
myEcho "  log      : $LOG"
myEcho "  database : $DBFILE"
myEcho "  awk file : $AWKFILE"
myEcho "  files to : $CASEDIR/logs"
myEcho ""


#-----------------------------
# Generate Awk program
#-----------------------------



#-- header

rm -f $AWKFILE; touch $AWKFILE
echo "BEGIN {" >> $AWKFILE
echo "    Iteration=0" >> $AWKFILE
echo "    resetCounters()" >> $AWKFILE
echo "}" >> $AWKFILE

echo "" >> $AWKFILE
echo "# reset counters used for variable postfix" >> $AWKFILE
echo "function resetCounters() {" >> $AWKFILE
for queryName in $QUERYNAMES
do
    varName=${queryName}Cnt
    echo "    ${varName}=0" >> $AWKFILE
done
echo "    # Reset counters for general Solving for extraction" >> $AWKFILE
echo "    for (varName in subIter)" >> $AWKFILE
echo "    {" >> $AWKFILE
echo "        subIter[varName]=0" >> $AWKFILE
echo "    }" >> $AWKFILE
echo "}" >> $AWKFILE
echo "" >> $AWKFILE


cat <<LABEL >> $AWKFILE
# Extract value after columnSel
function extract(inLine,columnSel,outVar,
                 a,b)
{
    a=index(inLine, columnSel)
    b=length(columnSel)
    split(substr(inLine, a+b),outVar)
    gsub("[,:]","",outVar[1])
}

LABEL





#-- Generate code for iteration separator (increments 'Iteration')
getQueries $DBFILE 'Separator'
cat <<LABSEP >> $AWKFILE
#-- Iteration separator (increments 'Iteration')
/$LINEQ/ {
    Iteration++
    resetCounters()
}

LABSEP


#-- Generate code for extracting Time
getQueries $DBFILE 'Time'
cat <<LABTIME >> $AWKFILE
#-- Time extraction (sets 'Time')
/$LINEQ/ {
    extract(\$0, "$NUMQ", val)
    Time=val[1]
}

LABTIME


#-- Generate code for singularity handling.
cat <<LABSING >> $AWKFILE
#-- Skip whole line with singularity variable
/solution singularity/ {
    next;
}
LABSING


#-- Generate code for extracting solved for quantities
cat <<LABSOLVE >> $AWKFILE
#-- Extraction of any solved for variable
/Solving for/ {
    extract(\$0, "Solving for ", varNameVal)

    varName=varNameVal[1]
    file=varName "_" subIter[varName]++
    file="$CASEDIR/logs/" file 
    extract(\$0, "Initial residual = ", val)
    print $TIMENAME "\t" val[1] > file

    varName=varNameVal[1] "FinalRes"
    file=varName "_" subIter[varName]++
    file="$CASEDIR/logs/" file 
    extract(\$0, "Final residual = ", val)
    print $TIMENAME "\t" val[1] > file

    varName=varNameVal[1] "Iters"
    file=varName "_" subIter[varName]++
    file="$CASEDIR/logs/" file 
    extract(\$0, "No Iterations ", val)
    print $TIMENAME "\t" val[1] > file
}

LABSOLVE

#-- generate code to process queries
for queryName in $QUERYNAMES
do
    getQueries $DBFILE $queryName
    if [ "$LINEQ" -a "$NUMQ" ]; then
        counter=${queryName}Cnt

        echo "#-- Extraction of $queryName" >> $AWKFILE
        echo "/$LINEQ/ {" >> $AWKFILE
        echo "    extract(\$0, \"$NUMQ\", val)" >> $AWKFILE
        echo "    file=\"$CASEDIR/logs/${queryName}_\" ${counter}" >> $AWKFILE
        echo "    print $TIMENAME \"\\t\" val[1] > file" >> $AWKFILE
        echo "    ${counter}++" >> $AWKFILE
        echo "}" >> $AWKFILE
        echo "" >> $AWKFILE
    fi
done



#-----------------------------
# Run awk program on log
#-----------------------------
(
    cmd="awk -f $AWKFILE $LOG"
    myEcho "Executing: $cmd"
    $cmd
    myEcho ""
)

#-----------------------------
# Print found
#-----------------------------
myEcho "Generated XY files for:"
getAllQueries $DBFILE $LOG

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