python - 如何制作基于文本的界面(TUI/TLI)来监控 OpenFOAM 求解器日志文件?

标签 python grep gnuplot tui openfoam

我正在集群上运行 OpenFOAM 模拟,需要几天时间才能完成。我正在寻找一种方法来监控流程并获得一些有意义的见解。我现在能做的就是使用

查看日志文件的尾部
watch tail -n 15 log.log

来自here我还发现了一个不错的 GnuPlot-grep 脚本:

set logscale y
set title "Residuals"
set ylabel 'Residual'
set xlabel 'Iteration'
plot "< cat log.log | grep 'Solving for Ux'    | cut -d' ' -f9 | tr -d ','" title 'Ux'                  with lines,\
     "< cat log.log | grep 'Solving for Uy'    | cut -d' ' -f9 | tr -d ','" title 'Uy'                  with lines,\
     "< cat log.log | grep 'Solving for Uz'    | cut -d' ' -f9 | tr -d ','" title 'Uz'                  with lines,\
     "< cat log.log | grep 'Solving for omega' | cut -d' ' -f9 | tr -d ','" title 'omega'               with lines,\
     "< cat log.log | grep 'Solving for k'     | cut -d' ' -f9 | tr -d ','" title 'k'                   with lines,\
     "< cat log.log | grep 'Solving for p'     | cut -d' ' -f9 | tr -d ','" title 'p'                   with lines,\
     "< cat log.log | grep 'Courant Number'    | cut -d' ' -f9 | tr -d ','" title 'Courant Number mean' with lines,\
     "< cat log.log | grep 'Courant Number'    | cut -d' ' -f6 | tr -d ','" title 'Courant Number max'  with lines
pause 1
reread

log.log 文件中提取信息,如果我在顶部某处添加 set term dump ,它可以在终端中绘制。然而,情节非常拥​​挤,丑陋,需要很长时间才能显示,并且按顺序打印到终端,而不是更新前一个。

在互联网上搜索,我发现有一些不错的 python 库,例如 npyscreen/picotui、ncurses/blessed、Asciimatics、Urwid、Prompt Toolkit ...用于创建 TUI/TLI。我想知道您是否可以帮助我了解如何创建基于文本的界面来显示基本信息以及所选值与时间的关系图。我想要几个面板。一个用于选择我想要绘制的变量,例如Courant Number Mean,另一个面板上有一个显示该变量与步骤时间的图。等实时显示所有变量的最新值。我的想法应该类似于 urwind's graph.py example :

enter image description here

附注自从我发布此内容以来:

  • Here我被介绍给 Termgraph一个非常有趣的 python 库,可以在终端中获取一些图形。
  • 我已将这个想法发布到 Urwid 谷歌群组中。您可以关注the discussion here
  • 我发现 PyFoamCaseBuilder 也使用 Urwid。另外here我获悉 PyFoam 项目中的其他尝试,旨在从求解器获取一些不错的 TUI 信息。

最佳答案

正如上面评论中所述,我为您制作了一些示例代码。它基于Redis,我建议您在集群管理器节点上运行Redis,该节点可能靠近集群节点并且始终处于运行状态 - 因此是一个很好的候选者统计数据收集服务。

示例代码是一个虚拟作业,用 Python 编写,监控例程用 bash 编写,但该作业也可以轻松地用 C/C++ 编写以及 Perl 中的监控例程 - Redis 有各种绑定(bind) - 不要沉迷于某种语言。

即使你看不懂Python,它也很容易理解。有3个线程并行运行。只需用总的处理时间更新 Redis 中的字符串即可。另外两个使用时间序列数据更新 Redis lists - 合成三角波 - 一个以 5 Hz 运行,另一个以 1 Hz 运行。

我使用了 Redis 字符串(变量不需要记录历史记录)和 Redis 列表(需要历史记录)。其他数据结构也可用。

在下面的代码中,唯一有趣的 3 行是:

# Connect to Redis server by IP address/name
r = redis.Redis(host='localhost', port=6379, db=0)

# Set a Redis string called 'processTime' to value `processsTime`
r.set('processTime', processTime)

# Push a value to left end of Redis list
r.lpush(RedisKeyName, value)

这是正在监视的虚拟作业。开始阅读它所说的内容

######
# Main
######

这是代码:

#!/usr/local/bin/python3

import redis
import _thread
import time
import os
import random

################################################################################
# Separate thread periodically updating the 'processTime' in Redis
################################################################################
def processTimeThread():
   """Calculate time since we started and update every so often in Redis"""
   start = time.time()
   while True:
      processTime = int(time.time() - start)
      r.set('processTime', processTime)
      time.sleep(0.2)

################################################################################
# Separate thread generating a times series and storing in Redis with the given
# name and update rate
################################################################################
def generateSeriesThread(RedisKeyName, interval):
   """Generate a saw-tooth time series and log to Redis"""
   # Delete any values from previous runs
   r.delete(RedisKeyName)
   value = 0
   inc = 1
   while True:
      # Generate next value and store in Redis
      value = value + inc
      r.lpush(RedisKeyName, value)
      if value == 0:
         inc = 1
      if value == 10:
         inc = -1
      time.sleep(interval)

################################################################################
# Main
################################################################################

# Connect to Redis on local host - but could just as easily be on another machine
r = redis.Redis(host='localhost', port=6379, db=0)

# Get start time of job in RFC2822 format
startTime=time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime())
# ... and set Redis string "startTime"
r.set('startTime',startTime)

# Get process id (pid)
pid=os.getpid()
# ... and set Redis string "pid""
r.set('pid',pid)

# Start some threads generating data
_thread.start_new_thread( processTimeThread, () )
_thread.start_new_thread( generateSeriesThread, ('seriesA', 0.2) )
_thread.start_new_thread( generateSeriesThread, ('seriesB', 1) )

# Hang around (with threads still running) till user presses a key
key = input("Press Return/Enter to stop.")

然后,我在 bash 中编写了一个监控脚本,该脚本连接到 Redis、获取值并以 TUI(文本用户界面)将它们显示在终端上。您同样可以使用 Python、Perl 或 PHP,并同样编写图形界面或基于 Web 的界面。

#!/bin/bash

################################################################################
# drawGraph
################################################################################
drawGraph(){
   top=$1 ; shift
   data=( "$@" )
   for ((row=0;row<10;row++)) ; do
      ((y=10-row))
      ((screeny=top+row))
      line=""
      for ((col=0;col<30;col++)) ; do
         char=" "
         declare -i v
         v=${data[col]}
         [ $v -eq $y ] && char="X"
         line="${line}${char}"
      done
      printf "$(tput cup $screeny 0)%s" "${line}"
   done
}

# Save screen and clear and make cursor invisible
tput smcup
tput clear
tput civis

# Trap exit
trap 'exit 1' INT TERM
trap 'tput rmcup; tput clear' EXIT

while :; do
   # Get processid from Redis and display
   pid=$(redis-cli <<< "get pid")
   printf "$(tput cup 0 0)ProcessId: $pid"

   # Get process start time from Redis and display
   startTime=$(redis-cli <<< "get startTime")
   printf "$(tput cup 1 0)Start Time: $startTime"

   # Get process running time from Redis and display
   processTime=$(redis-cli <<< "get processTime")
   printf "$(tput cup 2 0)Running Time: $(tput el)$processTime"

   # Display seriesA last few values
   seriesA=( $(redis-cli <<< "lrange seriesA 0 30") )
   printf "$(tput cup 5 0)seriesA latest values: $(tput el)"
   printf "%d " "${seriesA[@]}"

   # Display seriesB last few values
   seriesB=( $(redis-cli <<< "lrange seriesB 0 30") )
   printf "$(tput cup 6 0)seriesB latest values: $(tput el)"
   printf "%d " "${seriesB[@]}"

   drawGraph 8  "${seriesA[@]}"
   drawGraph 19 "${seriesB[@]}"

   # Put cursor at bottom of screen and tell user how to quit
   printf "$(tput cup 30 0)Hit Ctrl-C to quit"
done

希望您可以看到,您可以非常轻松地从 Redis 获取数据结构。这将获取集群节点上作业中设置的 processTime 变量:

processTime=$(redis-cli <<< "get processTime")

TUI 如下所示:

enter image description here

关于python - 如何制作基于文本的界面(TUI/TLI)来监控 OpenFOAM 求解器日志文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52074716/

相关文章:

python - 将 CSV 文件分割成相等的部分?

python - (fake_useragent) UserAgent() 不会连接

python - 如何使用setuptools安装到自定义目录?

linux - 临时使用awk或grep时如何将文本转换为小写并忽略空格?

gnuplot - 如何设置colorbox的位置?

for-loop - gnuplot:for和columnheader命令

python - 如何使用我的个人计算机在线托管 Python 套接字服务器?

linux - 为什么 pgrep 找不到这个进程?

linux - 解析大括号中的 block

java - GNUPlot 创建后立即消失