2 #-------------------------------------------------------------------------------
4 # | ____| _| |_ / __ \ /\ | \/ |
5 # | |__ _ __ ___ ___ / \| | | | / \ | \ / |
6 # | __| '__/ _ \/ _ ( (| |) ) | | |/ /\ \ | |\/| |
7 # | | | | | __/ __/\_ _/| |__| / ____ \| | | |
8 # |_| |_| \___|\___| |_| \____/_/ \_\_| |_|
10 # FreeFOAM: The Cross-Platform CFD Toolkit
12 # Copyright (C) 2008-2012 Michael Wild <themiwi@users.sf.net>
13 # Gerber van der Graaf <gerber_graaf@users.sf.net>
14 #-------------------------------------------------------------------------------
16 # This file is part of FreeFOAM.
18 # FreeFOAM is free software: you can redistribute it and/or modify it
19 # under the terms of the GNU General Public License as published by the
20 # Free Software Foundation, either version 3 of the License, or (at your
21 # option) any later version.
23 # FreeFOAM is distributed in the hope that it will be useful, but WITHOUT
24 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
25 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
28 # You should have received a copy of the GNU General Public License
29 # along with FreeFOAM. If not, see <http://www.gnu.org/licenses/>.
35 # Extracts info from log file
38 # -solution singularity not handled
39 #------------------------------------------------------------------------------
41 """Usage: freefoam@PY_SCRIPT_SUFFIX@ log [options] <log>
43 Extracts xy files from Foam logs.
47 -case <case_dir> Case directory (defaults to $PWD)
48 -n Produce single-column data files
50 -l Only list extracted variables
51 -h, -help Print this help message
52 <log> Log file from which to extract data. If <log> is not an
53 absolute path, it is relative to the specified case directory.
55 The default is to extract for all the 'Solved for' variables the initial
56 residual, the final residual and the number of iterations. On top of this a
57 (user editable) database of standard non-solved for variables is used to
58 extract data like Courant number, execution time.
60 The -l option shows all the possible variables but does not extract them.
62 The program writes a set of files, <case_dir>/logs/<var>_<subIter>, for every
63 <var> specified, for every occurrence inside a time step.
65 For variables that are 'Solved for' the initial residual name will be <var>,
66 the final residual will get name <var>FinalRes,
68 The files are a simple xy format with the first column Time (default) and the
69 second the extracted values. Option -n creates single column files with the
72 The query database is a simple text format containing Python regular
73 expressions. The regular expression must capture the queried value in a group
74 with the values name (i.e. using (?P<name>...) syntax). Lines where the first
75 non-blank character is a # will be ignored. The database will either be
76 <case_dir>/foamLog.db, $HOME/.FreeFOAM/foamLog.db or
77 @FOAM_DATA_DIR@/foamLog.db, whichever is found first.
79 Option -s suppresses the default information and only prints the extracted
89 # want to be future proof
90 sys
.path
.insert(0, '@FOAM_PYTHON_DIR@')
91 from FreeFOAM
.compat
import *
94 def __init__(self
, verbose
):
95 self
.verbose
= verbose
96 def __call__(self
, *args
):
100 class InvalidRegex(Exception):
101 """Raised if a regex fails to compile"""
102 def __init__(self
, regex
, i
, msg
):
103 """Initialize with the error message `msg`, thefailed regex `regex` in
105 Exception.__init
__(self
, regex
, i
, msg
)
108 return 'Failed to compile regular expression "%s" in line %d:\n %s'%self
.args
111 def getSolvedForRegex(logf
):
112 """Extracts from the file object `logf` the solved-for variables and
113 generates a list of regular expression objects to extract them or `None`."""
118 m
= re
.search(r
'Solving for\s+(?P<varname>\w+)', l
)
120 vars.add(m
.group('varname'))
126 result
.append(re
.compile((''.join([
127 'Solving for\s+%(var)s,\s+',
128 'Initial residual = (?P<%(var)s>\S+),\s+',
129 'Final residual = (?P<%(var)sFinalRes>\S+),\s+',
130 'No Iterations (?P<%(var)sIters>\S+)']))%{'var': v
}))
134 def getDbRegex(logf
, dbf
):
135 """Extracts from the file object `dbf` the contained regular expression
136 strings and returns a list with regular expression objects that match any of
137 the lines in the file object `logf` at least once."""
148 # try to compile the line (discarding empty and comment lines)
149 if not re
.match(r
'^\s*(#|$)', l
):
151 rc
= re
.compile(l
[:-1])
153 raise InvalidRegex(l
[:-1], i
, str(e
))
156 # try to match the regexes and tranfer the succesful ones into `result`
158 keys
= list(allRegex
.keys())
172 def resetCounters(counters
):
173 """Reset the sub-iter counters"""
174 for i
in counters
.iterkeys():
177 #-----------------------------
179 #-----------------------------
186 caseDir
= os
.getcwd()
199 elif a
== '-h' or a
== '-help':
204 sys
.stderr
.write('Error: -case requires argument\n')
205 sys
.stderr
.write(__doc__
+'\n')
210 sys
.stderr
.write('Error: unknown option "%s"\n'%a
)
211 sys
.stderr
.write(__doc__
+'\n')
217 plog
= PrintLog(not silent
)
220 sys
.stderr
.write('Error: No log file specified')
221 sys
.stderr
.write(__doc__
+'\n')
224 if not os
.path
.isabs(logName
):
225 logName
= os
.path
.join(caseDir
, logName
)
227 if not os
.path
.isfile(logName
):
228 sys
.stderr
.write('Error: No such file "%s"\n'%logName
)
235 os
.path
.expanduser('~/.FreeFOAM'),
236 os
.path
.normpath('@FOAM_DATA_DIR@')
238 n
= os
.path
.join(os
.path
.normpath(n
), 'foamLog.db')
239 if os
.path
.isfile(n
):
243 sys
.stderr
.write('Error: Failed to find foamLog.db\n')
246 # open the db and log file
247 logFile
= open(logName
, 'rt')
248 dbFile
= open(dbName
, 'rt')
250 # fetch all the regexes
251 regex
= getSolvedForRegex(logFile
)
252 regex
.extend(getDbRegex(logFile
, dbFile
))
255 # get all the variable names, create data and counter container
260 for n
in r
.groupindex
.iterkeys():
267 if len(vars) != len(set(vars)):
273 for v
, n
in cnt
.iteritems():
276 'Error: multiple regular expressions for variable "%s"\n'%v
)
279 # if -l specified, list variables
281 echo('\n'.join(vars))
285 plog(' log : %s'%logName
)
286 plog(' database : %s'%dbName
)
287 plog(' files to : %s/logs/'%caseDir
)
290 logsDir
= os
.path
.join(caseDir
, 'logs')
291 if not os
.path
.isdir(logsDir
):
292 if os
.path
.exists(logsDir
):
293 sys
.stderr
.write('Error: `%s` exists but is not a directory\n'%logsDir
)
297 # loop over lines, extract data
298 splitRegex
= re
.compile(r
'\s*Time\s*=\s*(?P<time>\S+)')
300 resetCounters(counters
)
303 # check for splitting regex
304 m
= splitRegex
.match(l
)
306 time
.append(m
.group('time'))
307 resetCounters(counters
)
310 # check for data regex
313 for n
, v
in m
.groupdict().iteritems():
314 while len(data
[n
]) <= counters
[n
]:
316 data
[n
][counters
[n
]].append(v
)
320 # loop over data and write
321 tLen
= max(map(len, time
))
322 for n
, v
in data
.iteritems():
323 for i
in xrange(len(v
)):
324 f
= open(os
.path
.join(logsDir
, '%s_%d'%(n
, i
)), 'wt')
325 for j
in xrange(len(time
)):
327 f
.write(('%-'+str(tLen
)+'s ')%time
[j
])
328 f
.write('%s\n'%v
[i
][j
])
330 plog('Generated XY files for:')
331 plog('\n'.join(vars))
333 # ------------------- vim: set sw=3 sts=3 ft=python et: ------------ end-of-file