1
2 """Base class for pyFoam-applications
3
4 Classes can also be called with a command-line string"""
5
6 from optparse import OptionGroup
7 from PyFoam.Basics.FoamOptionParser import FoamOptionParser
8 from PyFoam.Error import error,warning,FatalErrorPyFoamException
9 from PyFoam.FoamInformation import oldAppConvention as oldApp
10 from PyFoam.RunDictionary.SolutionDirectory import NoTouchSolutionDirectory
11
12 from PyFoam.Basics.TerminalFormatter import TerminalFormatter
13 from PyFoam import configuration
14
15 format=TerminalFormatter()
16 format.getConfigFormat("error")
17 format.getConfigFormat("warn")
18
19 import sys
20 from os import path,getcwd,environ
21 from copy import deepcopy
22
24 if hasattr(sys,'ps1'):
25 warning("Interactive mode. No debugger")
26 sys.__excepthook__(type,value,tb)
27 elif not (sys.stderr.isatty() and sys.stdin.isatty() and sys.stdout.isatty()):
28 warning("Not on a terminal. No debugger")
29 sys.__excepthook__(type,value,tb)
30 elif issubclass(type,SyntaxError):
31 warning("Syntax error. No debugger")
32 sys.__excepthook__(type,value,tb)
33 else:
34 import traceback,pdb
35 traceback.print_exception(type,value,tb)
36 print
37 pdb.pm()
38
40 - def __init__(self,
41 args=None,
42 description=None,
43 usage=None,
44 interspersed=False,
45 nr=None,
46 changeVersion=True,
47 exactNr=True,
48 inputApp=None):
49 """
50 @param description: description of the command
51 @param usage: Usage
52 @param interspersed: Is the command line allowed to be interspersed (options after the arguments)
53 @param args: Command line arguments when using the Application as a 'class' from a script
54 @param nr: Number of required arguments
55 @param changeVersion: May this application change the version of OF used?
56 @param exactNr: Must not have more than the required number of arguments
57 @param inputApp: Application with input data. Used to allow a 'pipe-like' behaviour if the class is used from a Script
58 """
59 self.parser=FoamOptionParser(args=args,
60 description=description,
61 usage=usage,
62 interspersed=interspersed)
63 self.generalOpts=None
64
65 self.__appData={}
66 if inputApp:
67 self.__appData["inputData"]=inputApp.getData()
68
69 grp=OptionGroup(self.parser,
70 "Default",
71 "Options common to all PyFoam-applications")
72
73 if changeVersion:
74
75 grp.add_option("--foamVersion",
76 dest="foamVersion",
77 default=None,
78 help="Change the OpenFOAM-version that is to be used")
79 if "WM_PROJECT_VERSION" in environ:
80 grp.add_option("--currentFoamVersion",
81 dest="foamVersion",
82 const=environ["WM_PROJECT_VERSION"],
83 default=None,
84 action="store_const",
85 help="Use the current OpenFOAM-version "+environ["WM_PROJECT_VERSION"])
86
87 grp.add_option("--force-32bit",
88 dest="force32",
89 default=False,
90 action="store_true",
91 help="Forces the usage of a 32-bit-version if that version exists as 32 and 64 bit. Only used when --foamVersion is used")
92 grp.add_option("--force-64bit",
93 dest="force64",
94 default=False,
95 action="store_true",
96 help="Forces the usage of a 64-bit-version if that version exists as 32 and 64 bit. Only used when --foamVersion is used")
97 grp.add_option("--force-debug",
98 dest="compileOption",
99 const="Debug",
100 default=None,
101 action="store_const",
102 help="Forces the value Debug for the WM_COMPILE_OPTION. Only used when --foamVersion is used")
103 grp.add_option("--force-opt",
104 dest="compileOption",
105 const="Opt",
106 default=None,
107 action="store_const",
108 help="Forces the value Opt for the WM_COMPILE_OPTION. Only used when --foamVersion is used")
109
110 grp.add_option("--psyco-accelerated",
111 dest="psyco",
112 default=False,
113 action="store_true",
114 help="Accelerate the script using the psyco-library (EXPERIMENTAL and requires a separatly installed psyco)")
115 grp.add_option("--profile-python",
116 dest="profilePython",
117 default=False,
118 action="store_true",
119 help="Profile the python-script (not the OpenFOAM-program) - mostly of use for developers")
120 grp.add_option("--profile-cpython",
121 dest="profileCPython",
122 default=False,
123 action="store_true",
124 help="Profile the python-script (not the OpenFOAM-program) using the better cProfile library - mostly of use for developers")
125 grp.add_option("--profile-hotshot",
126 dest="profileHotshot",
127 default=False,
128 action="store_true",
129 help="Profile the python-script using the hotshot-library (not the OpenFOAM-program) - mostly of use for developers - EXPERIMENTAL")
130 grp.add_option("--traceback-on-error",
131 dest="traceback",
132 default=False,
133 action="store_true",
134 help="Prints a traceback when an error is encountered (for debugging)")
135 grp.add_option("--interactive-debugger",
136 dest="interactiveDebug",
137 default=False,
138 action="store_true",
139 help="In case of an exception start the interactive debugger PDB. Also implies --traceback-on-error")
140 grp.add_option("--dump-application-data",
141 dest="dumpAppData",
142 default=False,
143 action="store_true",
144 help="Print the dictionary with the generated application data after running")
145 grp.add_option("--pickle-application-data",
146 dest="pickleApplicationData",
147 default=None,
148 action="store",
149 type="string",
150 help="""\
151 Write a pickled version of the application data to a file. If the
152 filename given is 'stdout' then the pickled data is written to
153 stdout. The usual standard output is then captured and added to the
154 application data as an entry 'stdout' (same for 'stderr'). Be careful
155 with these option for commands that generate a lot of output""")
156
157 self.parser.add_option_group(grp)
158
159 self.addOptions()
160 self.parser.parse(nr=nr,exactNr=exactNr)
161 self.opts=self.parser.getOptions()
162
163 if self.opts.interactiveDebug:
164 sys.excepthook=pyFoamExceptionHook
165 self.opts.traceback=True
166
167 if self.opts.psyco:
168 try:
169 import psyco
170 psyco.full()
171 except ImportError:
172 warning("No psyco installed. Continuing without acceleration")
173
174 if self.opts.profilePython or self.opts.profileCPython or self.opts.profileHotshot:
175 if sum([self.opts.profilePython,self.opts.profileCPython,self.opts.profileHotshot])>1:
176 self.error("Profiling with hotshot and regular profiling are mutual exclusive")
177 print "Running profiled"
178 if self.opts.profilePython:
179 import profile
180 elif self.opts.profileCPython:
181 import cProfile as profile
182 else:
183 import hotshot
184 profileData=path.basename(sys.argv[0])+".profile"
185 if self.opts.profilePython or self.opts.profileCPython:
186 profile.runctx('self.run()',None,{'self':self},profileData)
187 print "Reading python profile"
188 import pstats
189 stats=pstats.Stats(profileData)
190 else:
191 profileData+=".hotshot"
192 prof=hotshot.Profile(profileData)
193 prof.runctx('self.run()',{},{'self':self})
194 print "Writing and reading hotshot profile"
195 prof.close()
196 import hotshot.stats
197 stats=hotshot.stats.load(profileData)
198 stats.strip_dirs()
199 stats.sort_stats('time','calls')
200 stats.print_stats(20)
201
202 self.parser.restoreEnvironment()
203 else:
204 try:
205 if self.opts.pickleApplicationData=="stdout":
206
207 import StringIO
208 oldStdout=sys.stdout
209 oldStderr=sys.stderr
210 sys.stdout=StringIO.StringIO()
211 sys.stderr=StringIO.StringIO()
212
213 result=self.run()
214
215
216 self.parser.restoreEnvironment()
217
218 if self.opts.pickleApplicationData=="stdout":
219
220 self.__appData["stdout"]=sys.stdout.getvalue()
221 self.__appData["stderr"]=sys.stderr.getvalue()
222 sys.stdout=oldStdout
223 sys.stderr=oldStderr
224
225 if self.opts.pickleApplicationData:
226 import cPickle as pickle
227 if self.opts.pickleApplicationData=="stdout":
228 pick=pickle.Pickler(sys.stdout)
229 else:
230 pick=pickle.Pickler(open(self.opts.pickleApplicationData,'w'))
231 pick.dump(self.__appData)
232 del pick
233 if self.opts.dumpAppData:
234 import pprint
235 print "Application data:"
236 printer=pprint.PrettyPrinter()
237 printer.pprint(self.__appData)
238
239 return result
240 except FatalErrorPyFoamException,e:
241 if self.opts.traceback:
242 raise
243 else:
244 self.error(e.descr)
245
247 """Get application data"""
248 try:
249 return self.__appData[key]
250 except KeyError:
251 print "available keys:",self.__appData.keys()
252 raise
253
255 """Iterate over the application data"""
256 for k in self.__appData:
257 yield k
258
261
264
266 """Get the application data"""
267 return deepcopy(self.__appData)
268
270 """Set the application data
271
272 @param data: dictionary whose entries will be added to the
273 application data (possibly overwriting old entries of the same name)"""
274 for k,v in data.iteritems():
275 self.__appData[k]=deepcopy(v)
276
278 if self.generalOpts==None:
279 self.generalOpts=OptionGroup(self.parser,
280 "General",
281 "General options for the control of OpenFOAM-runs")
282 self.parser.add_option_group(self.generalOpts)
283
285 """
286 Add options to the parser
287 """
288 pass
289
291 """
292 Run the real application
293 """
294 error("Not a valid application")
295
296
298 """
299 Prints an error message and exits
300 @param args: Arguments that are to be printed
301 """
302 print format.error+"Error in",sys.argv[0],":",
303 for a in args:
304 print a,
305 print format.reset
306 sys.exit(-1)
307
309 """
310 Prints a warning message
311 @param args: Arguments that are to be printed
312 """
313 print format.warn+"Warning in",sys.argv[0],":",
314 for a in args:
315 print a,
316 print format.reset
317
319 """
320 Don't print a warning message
321 @param args: Arguments that are to be printed
322 """
323 pass
324
325 - def checkCase(self,name,fatal=True,verbose=True):
326 """
327 Check whether this is a valid OpenFOAM-case
328 @param name: the directory-bame that is supposed to be the case
329 @param fatal: If this is not a case then the application ends
330 @param verbose: If this is not a case no warning is issued
331 """
332 if fatal:
333 func=self.error
334 elif verbose:
335 func=self.warning
336 else:
337 func=self.silent
338
339 if not path.exists(name):
340 func("Case",name,"does not exist")
341 return False
342 if not path.isdir(name):
343 func("Case",name,"is not a directory")
344 return False
345 if not path.exists(path.join(name,"system")):
346 func("Case",name,"does not have a 'system' directory")
347 return False
348 if not path.exists(path.join(name,"constant")):
349 func("Case",name,"does not have a 'constant' directory")
350 return False
351
352 return True
353
364
371