1
2 """Working with a directory of samples"""
3
4 from os import path,listdir
5 from PyFoam.Error import error
6 import math
7 import re
8
9 from PyFoam.Basics.SpreadsheetData import SpreadsheetData
10
12 """A directory of sampled times"""
13
14 - def __init__(self,
15 case,
16 dirName="samples",
17 postfixes=[],
18 prefixes=[]):
19 """@param case: The case directory
20 @param dirName: Name of the directory with the samples
21 @param postfixes: list of possible extensions to a field name of the form
22 name_postfix to help splitting such field names.
23 @param prefixes: list of possible extensions to a field name of the form
24 prefix_name to help splitting such field names"""
25
26 self.dir=path.join(case,dirName)
27 self.times=[]
28
29 self.prefixes=prefixes
30 self.postfixes=postfixes
31
32 for d in listdir(self.dir):
33 if path.isdir(path.join(self.dir,d)):
34 try:
35 float(d)
36 self.times.append(d)
37 except ValueError:
38 pass
39
40 self.times.sort(key=float)
41
43 return len(self.times)
44
46 for t in self.times:
47 yield SampleTime(self.dir,
48 t,
49 prefixes=self.prefixes,
50 postfixes=self.postfixes)
51
53 if time in self:
54 return SampleTime(self.dir,
55 time,
56 prefixes=self.prefixes,
57 postfixes=self.postfixes)
58 else:
59 raise KeyError(time)
60
62 return time in self.times
63
76
89
90 - def getData(self,
91 line=None,
92 value=None,
93 time=None,
94 note="",
95 scale=(1,1),
96 offset=(0,0)):
97 """Get Sample sets
98 @param line: name of the line. All
99 if unspecified
100 @param value: name of the sampled value. All
101 if unspecified
102 @param time: times for which the samples are to be got. All
103 if unspecified
104 @param note: A short annotation (for plots)
105 @param scale: pair of factors with which the data is scaled when being plotted
106 @param offset: pair of offsets"""
107
108 if line==None:
109 line=self.lines()
110 if value==None:
111 value=list(self.values())
112 if time==None:
113 time=self.times
114
115 sets=[]
116
117 for t in time:
118 for l in line:
119 for v in value:
120 try:
121 d=self[t][(l,v)]
122 if d==None:
123 continue
124 d.note=note
125 d.scale=scale
126 d.offset=offset
127 sets.append(d)
128 except KeyError:
129 pass
130
131 return sets
132
134 """A directory with one sampled time"""
135
136 - def __init__(self,
137 sDir,
138 time,
139 postfixes=[],
140 prefixes=[]):
141 """@param sDir: The sample-dir
142 @param time: the timename
143 @param postfixes: list of possible extensions to a field name of the form
144 name_postfix to help splitting such field names.
145 @param prefixes: list of possible extensions to a field name of the form
146 prefix_name to help splitting such field names"""
147
148 self.dir=path.join(sDir,time)
149 self.lines=[]
150 self.values=[]
151
152 self.prefixes=prefixes
153 self.postfixes=postfixes
154
155 self.__valueNames=None
156
157 for f in listdir(self.dir):
158 if f[0]=='.' or f[-1]=='~' or f.find(".")<0:
159 continue
160 nm=self.extractLine(f)
161 vals=self.extractValues(f)
162 if nm not in self.lines:
163 self.lines.append(nm)
164 for v in vals:
165 if v not in self.values:
166 self.values.append(v)
167
168 self.lines.sort()
169 self.values.sort()
170
171 self.cache={}
172
174 """Extract the name of the line from a filename"""
175 return fName.split("_")[0]
176
178 """Extracts the names of the contained Values from a filename"""
179
180 def preUnder(m):
181 return "&"+m.group(1)+m.group(2)
182 def postUnder(m):
183 return m.group(1)+m.group(2)+"&"
184
185 for p in self.prefixes:
186 fName=re.sub("([_&.]|^)("+p+")_",postUnder,fName)
187 for p in self.postfixes:
188 fName=re.sub("_("+p+")([_&.]|$)",preUnder,fName)
189
190 self.__valueNames=[]
191 try:
192 tmp=fName.split("_")[1:]
193 tmp[-1]=tmp[-1].split(".")[0]
194
195 for t in tmp:
196 self.__valueNames.append(t.replace("&","_"))
197 except IndexError:
198 pass
199
200 return self.__valueNames
201
203 """Get the data for a value on a specific line
204 @param key: A tuple with the line-name and the value-name
205 @returns: A SampleData-object"""
206
207 if key in self.cache:
208 return self.cache[key]
209
210 line,val=key
211 if line not in self.lines or val not in self.values:
212 raise KeyError(key)
213
214 fName=None
215
216 for f in listdir(self.dir):
217 if line==self.extractLine(f) and val in self.extractValues(f):
218 fName=f
219 break
220
221 if fName==None:
222 error("Can't find a file for the line",line,"and the value",val,"in the directory",self.dir)
223
224 first=True
225 col0=[]
226 data=[]
227 index=None
228
229 for l in open(path.join(self.dir,fName)).readlines():
230 tmp=l.split()
231 if first:
232 first=False
233 vector,index=self.determineIndex(fName,val,tmp)
234
235 col0.append(float(tmp[0]))
236 try:
237 if vector:
238 data.append(tuple(map(float,tmp[index:index+3])))
239 else:
240 data.append(float(tmp[index]))
241 except IndexError:
242 raise KeyError(key)
243
244 if index!=None:
245 self.cache[key]=SampleData(fName=path.join(self.dir,fName),
246 name=val,
247 index=index,
248 col0=col0,
249 data=data)
250
251 return self.cache[key]
252 else:
253 return None
254
256 """Determines the index of the data from the filename and a dataset
257 @param fName: name of the file
258 @param vName: Name of the quantity
259 @param data: A list with the data
260 @returns: A tuple of a boolean (whether the data is supposed to be
261 a vector or a scalar) and an integer (the index of the data set -
262 or the first component of the vector"""
263
264 vals=self.extractValues(fName)
265 if len(vals)+1==len(data):
266 vector=False
267 elif len(vals)*3+1==len(data):
268 vector=True
269 else:
270 error("The data in file",fName,"is neither vector nor scalar:",data)
271
272 index=vals.index(vName)
273 if vector:
274 index=index*3+1
275 else:
276 index=index+1
277
278 return vector,index
279
281 """Data from a sample-set"""
282
283 - def __init__(self,
284 fName,
285 name,
286 index,
287 col0,
288 data,
289 note="",
290 scale=(1,1),
291 offset=(0,0)):
292 """@param fName: Name of the file
293 @param name: Name of the value
294 @param index: Index of the data in the file
295 @param col0: Values that identify the data (the location)
296 @param data: The actual data
297 @param scale: pair of factors with which the data is scaled when being plotted
298 @param offset: pair of offsets"""
299
300 self.file=fName
301 self.col0=col0
302 self.data=data
303 self.name=name
304 self.index=index
305 self.note=note
306 self.scale=scale
307 self.offset=offset
308
310 if self.isVector():
311 vect=" (vector)"
312 else:
313 vect=""
314
315 return "SampleData of %s%s on %s at t=%s " % (self.name,vect,self.line(),self.time())
316
318 """Get the line of the sample"""
319 return path.basename(self.file).split("_")[0]
320
322 """Get the time of the sample (as a string)"""
323 return path.basename(path.dirname(self.file))
324
326 """Is this vector or scalar data?"""
327 if type(self.data[0])==tuple:
328 return True
329 else:
330 return False
331
332 - def range(self,component=None):
337
339 """Range of the data domain"""
340 return (min(self.col0),max(self.col0))
341
343 """Return the data as a number of single scalars.
344 @param component: If None for vectors the absolute value is taken.
345 else the number of the component"""
346
347 if self.isVector():
348 data=[]
349 if component==None:
350 for d in self.data:
351 data.append(math.sqrt(d[0]*d[0]+d[1]*d[1]+d[2]*d[2]))
352 else:
353 if component<0 or component>=len(self.data[0]):
354 error("Requested component",component,"does not fit the size of the data",len(self.data[0]))
355 for d in self.data:
356 data.append(d[component])
357 return data
358 else:
359 return self.data
360
361 - def __call__(self,
362 scaleX=1.,
363 scaleData=1,
364 offsetData=0,
365 offsetX=0):
366 """Return the data as SpreadsheetData-object"""
367
368 data=[]
369 if self.isVector():
370 for i,c in enumerate(self.col0):
371 data.append([scaleX*c+offsetX]+[scaleData*v+offsetData for v in self.data[i]])
372 else:
373 for i,c in enumerate(self.col0):
374 data.append([scaleX*c+offsetX,scaleData*self.data[i]+offsetData])
375
376 names=["col0"]
377 if self.isVector():
378 names+=[self.name+"_x",self.name+"_y",self.name+"_z"]
379 else:
380 names.append(self.name)
381
382 return SpreadsheetData(data=data,
383 names=names,
384 title="%s_t=%s" % (self.line(),self.time()))
385
386
387