326 lines
9.2 KiB
Bash
Executable File
326 lines
9.2 KiB
Bash
Executable File
#!/bin/sh
|
|
#------------------------------------------------------------------------------
|
|
# ========= |
|
|
# \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
|
# \\ / O peration | Website: https://openfoam.org
|
|
# \\ / A nd | Copyright (C) 2015-2024 OpenFOAM Foundation
|
|
# \\/ 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
|
|
# foamMonitor
|
|
#
|
|
# Description
|
|
# Displays graphs to monitor data from files written by OpenFOAM, e.g.
|
|
# by functionObjects. Requires gnuplot to be installed.
|
|
#
|
|
#------------------------------------------------------------------------------
|
|
|
|
usage() {
|
|
cat<<USAGE
|
|
|
|
Usage: ${0##*/} [OPTION] <file>
|
|
options:
|
|
-ascii | -a show the graph by printing ascii in the terminal
|
|
-columns | -c <cols> display specfied columns, comma-separated, e.g. "2,4"
|
|
-flip | -f plot data on x-axis, independent variable on y-axis
|
|
-help | -h print the usage
|
|
-idle | -i <time> stops if <file> unchanging for <time> sec (default 60s)
|
|
-logscale | -l plots data (y-axis) on log scale, e.g. for residuals
|
|
-once | -o print a graph one time without refreshing
|
|
-refresh | -r <time> refreshes display every <time> sec (default 10s)
|
|
-size | -s <size> set the size of the output plot, format "640,480"
|
|
-title | -t <title> set the graph title (default "Data Monitoring")
|
|
-yrange | -y <range> sets data (y-axis) range, format "[0:1]"
|
|
|
|
Displays graphs to monitor data from files written by OpenFOAM, e.g. by
|
|
functionObjects. Requires gnuplot to be installed.
|
|
|
|
Graphs are plotted from column data files written by OpenFOAM, where column 1 is
|
|
the independent variable, e.g. time, plotted on the horizontal axis by default.
|
|
The other columns are the dependent data, plotted on the vertical axis by
|
|
default. The data can be plotted within a specified range, using the '-r' option
|
|
and/or on a log scale using the '-l' option. The default axes can be flipped by
|
|
the '-f' option (with data plotted on the horizontal axis).
|
|
|
|
In standard monitoring mode, graphs are refreshed (default: every 10s).
|
|
The graph stops refreshing when the data file is unchanged for a prolongued
|
|
period (default: 60s). Graphs can also be printed one time without refreshing
|
|
using the '-o' option.
|
|
|
|
Examples:
|
|
+ Monitor residuals on a log scale, running in background ('&')
|
|
foamMonitor -l postProcessing/residuals/0/residuals.dat &
|
|
+ Monitor probed pressure values from column 2, also in background
|
|
foamMonitor -c 2 postProcessing/probes/0/p &
|
|
+ Plot force coefficients from column 3 once, e.g. after the simulation stops
|
|
foamMonitor -o -c 3 postProcessing/forces/0/forceCoeffs.dat
|
|
|
|
USAGE
|
|
}
|
|
|
|
error() {
|
|
exec 1>&2
|
|
while [ "$#" -ge 1 ]; do echo "$1"; shift; done
|
|
usage
|
|
exit 1
|
|
}
|
|
|
|
plotFileHeader() {
|
|
_term="$1"
|
|
_size="$2"
|
|
_logscale="$3"
|
|
_range="$4"
|
|
_title="$5"
|
|
_label="$6"
|
|
|
|
_ticFormat="%g"
|
|
[ "$logscale" = yes ] && _ticFormat="%1.e"
|
|
_otherAxis="$([ "$AXIS" = x ] && echo y || echo x)"
|
|
|
|
printf "set term %s" "$_term"
|
|
[ "$_size" = default ] || printf " size %s" "$_size"
|
|
printf "\n"
|
|
|
|
[ "$_logscale" = yes ] && printf "set logscale %s\n" "$AXIS"
|
|
[ "$_range" = none ] || printf "set %srange %s\n" "$AXIS" "$_range"
|
|
printf "set %stics format \"%s\"\n" "$AXIS" "$_ticFormat"
|
|
printf "set title \"%s\"\n" "$_title"
|
|
printf "set %slabel \"%s\"\n" "$_otherAxis" "$_label"
|
|
echo "plot \\"
|
|
}
|
|
|
|
plotFileFooter() {
|
|
cat<<EOF
|
|
|
|
pause $refresh
|
|
reread
|
|
EOF
|
|
}
|
|
|
|
howMany() ( echo "$1" | awk '{ print NF }' )
|
|
|
|
isInt() {
|
|
[ "$1" -eq "$1" ] 2> /dev/null
|
|
}
|
|
|
|
flip() {
|
|
[ "$AXIS" = y ] && echo "$1" && return 0
|
|
echo "$1" | awk -F: '{print $2 ":" $1}'
|
|
}
|
|
|
|
# Check gnuplot is insalled
|
|
command -v gnuplot >/dev/null 2>&1 || error "Gnuplot not installed"
|
|
|
|
# Variables used globally
|
|
AXIS="y"
|
|
|
|
# Variables used locally
|
|
term='x11 1 font "helvetica,17" linewidth 1.5 persist noraise'
|
|
size="default"
|
|
logscale="no"
|
|
range="none"
|
|
title="Data Monitoring"
|
|
|
|
idle=60
|
|
refresh=10
|
|
once=""
|
|
columns=""
|
|
|
|
# Options
|
|
while [ "$#" -gt 0 ]
|
|
do
|
|
case "$1" in
|
|
-a | -ascii)
|
|
term="dumb"
|
|
shift 1
|
|
;;
|
|
-c | -columns)
|
|
[ "$#" -ge 2 ] || error "'$1' option requires an argument"
|
|
columns=$(echo "$2" | tr , " ")
|
|
for c in $columns
|
|
do isInt "$c" || error "Argument of '$1' is not an integer: '$c'"
|
|
done
|
|
shift 2
|
|
;;
|
|
-f | -flip)
|
|
AXIS="x"
|
|
shift 1
|
|
;;
|
|
-h | -help)
|
|
usage && exit 0
|
|
;;
|
|
-i | -idle)
|
|
[ "$#" -ge 2 ] || error "'$1' option requires an argument"
|
|
isInt "$2" || error "Argument of '$1' is not an integer: '$2'"
|
|
idle="$2"
|
|
shift 2
|
|
;;
|
|
-l | -logscale)
|
|
logscale="yes"
|
|
shift 1
|
|
;;
|
|
-o | -once)
|
|
once="yes"
|
|
shift 1
|
|
;;
|
|
-r | -refresh)
|
|
[ "$#" -ge 2 ] || error "'$1' option requires an argument"
|
|
isInt "$2" || error "Argument of '$1' is not an integer: '$2'"
|
|
refresh="$2"
|
|
shift 2
|
|
;;
|
|
-s | -size)
|
|
[ "$#" -ge 2 ] || error "'$1' option requires an argument"
|
|
size="$2"
|
|
shift 2
|
|
;;
|
|
-t | -title)
|
|
[ "$#" -ge 2 ] || usage "'$1' option requires an argument"
|
|
title=$2
|
|
shift 2
|
|
;;
|
|
-y | -yrange)
|
|
[ "$#" -ge 2 ] || error "'$1' option requires an argument"
|
|
range="$2"
|
|
shift 2
|
|
;;
|
|
-*)
|
|
error "unknown option: '$*'"
|
|
;;
|
|
*)
|
|
break
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Settings for one graph, without refresh
|
|
[ "$idle" -eq 0 ] && refresh=0
|
|
[ "$once" ] && idle=0 && refresh=0
|
|
|
|
# Check input file
|
|
[ $# -eq 1 ] || error "Incorrect arguments specified"
|
|
[ -f "$1" ] || error "File $1 does not exist"
|
|
file=$1
|
|
|
|
# Get keys from file header
|
|
keys=$(grep -E '^#' "$file" | tail -1 | tr -s " ")
|
|
|
|
# If no header, use 'Step' to describe column 1
|
|
[ "$keys" = "" ] && keys="# Step"
|
|
|
|
n_keys=$(howMany "$keys")
|
|
n_cols=$(tail -1 "$file" | awk '{ print NF }')
|
|
|
|
# With full column labels, n_keys = n_cols + 1, since it includes "#"
|
|
# If n_keys > n_cols + 1, REMOVE EXCESS keys
|
|
n_cols_pone=$(( n_cols + 1 ))
|
|
[ "$n_keys" -gt "$n_cols_pone" ] && \
|
|
keys=$(echo "$keys" | cut -d" " -f1-$n_cols_pone)
|
|
n_keys=$(howMany "$keys")
|
|
|
|
# If n_keys < n_cols + 1, create keys 'data1', 'data2', etc
|
|
i=0
|
|
while [ "$n_keys" -le "$n_cols" ]
|
|
do
|
|
i=$(( i + 1 ))
|
|
keys="$keys data$i"
|
|
n_keys=$(howMany "$keys")
|
|
done
|
|
|
|
# Remove starting "#"; set 'n_keys' finally
|
|
keys=$(echo "$keys" | cut -d " " -f2-)
|
|
n_keys=$(howMany "$keys")
|
|
|
|
# Set 'label' to column 1 key
|
|
label=$(echo "$keys" | awk '{print $1}')
|
|
|
|
# Set columns to display
|
|
[ "$columns" ] || columns="$(seq 2 "$n_keys")"
|
|
|
|
# Check the highest column is not out of range
|
|
highest_column="$(echo "$columns" | xargs -n 1 | \
|
|
sort -n | xargs | awk '{print $NF}')"
|
|
[ "$highest_column" -gt "$n_keys" ] && \
|
|
error "Data does not extend to specified column '$highest_column'"
|
|
|
|
# Last column
|
|
# shellcheck disable=SC2086
|
|
last_col="$(echo $columns | awk '{print $NF}')"
|
|
|
|
# START Gnuplot file
|
|
gp_file=$(mktemp /tmp/tmp.foamMonitor.XXXXXX)
|
|
|
|
plotFileHeader \
|
|
"$term" \
|
|
"$size" \
|
|
"$logscale" \
|
|
"$range" \
|
|
"$title" \
|
|
"$label" > "$gp_file"
|
|
|
|
for col in $columns
|
|
do
|
|
field="$(echo "$keys" | awk -v c="$col" '{print $c}')"
|
|
plot_line="\"$file\" using $(flip "1:${col}") with lines title \"$field\""
|
|
[ "$col" -ne "$last_col" ] && plot_line="$plot_line, \\"
|
|
echo "$plot_line" >> "$gp_file"
|
|
done
|
|
|
|
# Add re-read footer if re-reading file
|
|
[ "$idle" -eq 0 ] || plotFileFooter >> "$gp_file"
|
|
# END Gnuplot file
|
|
|
|
gnuplot "$gp_file" &
|
|
pid=$!
|
|
|
|
# Wait for gnuplot to spawn a process (xterm)
|
|
! [ "$term" = "dumb" ] && \
|
|
start="$(date +%s)"
|
|
while ! [ "$(pgrep -P $pid)" ]
|
|
do
|
|
# timeout after 2 seconds in event xterm is not spawned
|
|
now="$(date +%s)"
|
|
[ $(( now - start )) -gt 2 ] && break
|
|
sleep .1
|
|
done
|
|
|
|
# Get the PID of the spawned process
|
|
ch_pid="$(pgrep -P $pid)"
|
|
|
|
# Kill gnuplot/gnuplot_x11 and delete tmp file, then kill foamMonitor on trap
|
|
trap 'kill -9 $pid $ch_pid; rm -f $gp_file; kill -9 $$' INT TERM
|
|
trap 'rm -f $gp_file' EXIT
|
|
|
|
# Display graph once only
|
|
[ "$idle" -eq 0 ] && exit 0
|
|
|
|
# Update timestamp on data file
|
|
touch "$file"
|
|
|
|
# Re-read data file to update graph
|
|
while true
|
|
do
|
|
mod_time=$(stat --format=%Y "$file")
|
|
idle_ago=$(( $(date +%s) - idle ))
|
|
test "$mod_time" -gt "$idle_ago" || break
|
|
sleep "$refresh"
|
|
done
|
|
|
|
#------------------------------------------------------------------------------
|