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 e
= sys
.exc_info()[1]
154 raise InvalidRegex(l
[:-1], i
, str(e
))
157 # try to match the regexes and tranfer the succesful ones into `result`
159 keys
= list(allRegex
.keys())
173 def resetCounters(counters
):
174 """Reset the sub-iter counters"""
175 for i
in counters
.keys():
178 #-----------------------------
180 #-----------------------------
187 caseDir
= os
.getcwd()
200 elif a
== '-h' or a
== '-help':
205 sys
.stderr
.write('Error: -case requires argument\n')
206 sys
.stderr
.write(__doc__
+'\n')
211 sys
.stderr
.write('Error: unknown option "%s"\n'%a
)
212 sys
.stderr
.write(__doc__
+'\n')
218 plog
= PrintLog(not silent
)
221 sys
.stderr
.write('Error: No log file specified')
222 sys
.stderr
.write(__doc__
+'\n')
225 if not os
.path
.isabs(logName
):
226 logName
= os
.path
.join(caseDir
, logName
)
228 if not os
.path
.isfile(logName
):
229 sys
.stderr
.write('Error: No such file "%s"\n'%logName
)
236 os
.path
.expanduser('~/.FreeFOAM'),
237 os
.path
.normpath('@FOAM_DATA_DIR@')
239 n
= os
.path
.join(os
.path
.normpath(n
), 'foamLog.db')
240 if os
.path
.isfile(n
):
244 sys
.stderr
.write('Error: Failed to find foamLog.db\n')
247 # open the db and log file
248 logFile
= open(logName
, 'rt')
249 dbFile
= open(dbName
, 'rt')
251 # fetch all the regexes
252 regex
= getSolvedForRegex(logFile
)
253 regex
.extend(getDbRegex(logFile
, dbFile
))
256 # get all the variable names, create data and counter container
261 for n
in r
.groupindex
.keys():
268 if len(vars) != len(set(vars)):
274 for v
, n
in cnt
.items():
277 'Error: multiple regular expressions for variable "%s"\n'%v
)
280 # if -l specified, list variables
282 echo('\n'.join(vars))
285 logsDir
= os
.path
.join(caseDir
, 'logs')
288 plog(' log : %s'%logName
)
289 plog(' database : %s'%dbName
)
290 plog(' files to : %s'%logsDir
)
293 if not os
.path
.isdir(logsDir
):
294 if os
.path
.exists(logsDir
):
295 sys
.stderr
.write('Error: `%s` exists but is not a directory\n'%logsDir
)
299 # loop over lines, extract data
300 splitRegex
= re
.compile(r
'\s*Time\s*=\s*(?P<time>\S+)')
302 resetCounters(counters
)
305 # check for splitting regex
306 m
= splitRegex
.match(l
)
308 time
.append(m
.group('time'))
309 resetCounters(counters
)
313 # check for data regex
316 for n
, v
in m
.groupdict().items():
317 while len(data
[n
]) <= counters
[n
]:
319 data
[n
][counters
[n
]].append(v
)
323 # loop over data and write
324 tLen
= max(map(len, time
))
325 for n
, v
in data
.items():
326 for i
in range(len(v
)):
327 f
= open(os
.path
.join(logsDir
, '%s_%d'%(n
, i
)), 'wt')
328 for j
in range(min(len(time
), len(v
[i
]))):
330 f
.write(('%-'+str(tLen
)+'s ')%time
[j
])
331 f
.write('%s\n'%v
[i
][j
])
333 plog('Generated XY files for:')
334 plog('\n'.join(vars))
336 # ------------------- vim: set sw=3 sts=3 ft=python et: ------------ end-of-file