Package PyFoam :: Package RunDictionary :: Module SampleDirectory
[hide private]
[frames] | no frames]

Source Code for Module PyFoam.RunDictionary.SampleDirectory

  1  #  ICE Revision: $Id:$ 
  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   
11 -class SampleDirectory(object):
12 """A directory of sampled times""" 13
14 - def __init__(self, 15 case, 16 dirName="samples", 17 postfixes=[], 18 prefixes=[], 19 valueNames=None, 20 linePattern=None, 21 namesFromFirstLine=False, 22 needsExtension=True):
23 """@param case: The case directory 24 @param dirName: Name of the directory with the samples 25 @param postfixes: list of possible extensions to a field name of the form 26 name_postfix to help splitting such field names. 27 @param prefixes: list of possible extensions to a field name of the form 28 prefix_name to help splitting such field names 29 @param valueNames: List of value names. If specified then the classes do 30 not try to determine the names automatically 31 @param linePattern: Regular expression to determine the name of the line 32 from the filename. The first group in the expression is the name. If unset 33 the linename is determined automatically 34 @param needsExtension: whether a file needs an extension""" 35 36 self.dir=path.join(case,dirName) 37 self.times=[] 38 39 self.__defaultNames=valueNames 40 self.__linePattern=linePattern 41 self.__needsExtension=needsExtension 42 self.__namesFromFirstLine=namesFromFirstLine 43 44 self.prefixes=prefixes 45 self.postfixes=postfixes 46 47 for d in listdir(self.dir): 48 if path.isdir(path.join(self.dir,d)): 49 try: 50 float(d) 51 self.times.append(d) 52 except ValueError: 53 pass 54 55 self.times.sort(key=float)
56
57 - def __len__(self):
58 return len(self.times)
59
60 - def __iter__(self):
61 for t in self.times: 62 yield SampleTime(self.dir, 63 t, 64 prefixes=self.prefixes, 65 postfixes=self.postfixes, 66 valueNames=self.__defaultNames, 67 namesFromFirstLine=self.__namesFromFirstLine, 68 linePattern=self.__linePattern, 69 needsExtension=self.__needsExtension)
70
71 - def __getitem__(self,time):
72 if time in self: 73 return SampleTime(self.dir, 74 time, 75 prefixes=self.prefixes, 76 postfixes=self.postfixes, 77 valueNames=self.__defaultNames, 78 namesFromFirstLine=self.__namesFromFirstLine, 79 linePattern=self.__linePattern, 80 needsExtension=self.__needsExtension) 81 else: 82 raise KeyError(time)
83
84 - def __contains__(self,time):
85 return time in self.times
86
87 - def lines(self):
88 """Returns all the found sample lines""" 89 90 lines=[] 91 92 for t in self: 93 for l in t.lines: 94 if l not in lines: 95 lines.append(l) 96 lines.sort() 97 98 return lines
99
100 - def values(self):
101 """Returns all the found sampled values""" 102 103 values=[] 104 105 for t in self: 106 for v in t.values: 107 if v not in values: 108 values.append(v) 109 values.sort() 110 111 return values
112
113 - def getData(self, 114 line=None, 115 value=None, 116 time=None, 117 note="", 118 scale=(1,1), 119 offset=(0,0)):
120 """Get Sample sets 121 @param line: name of the line. All 122 if unspecified 123 @param value: name of the sampled value. All 124 if unspecified 125 @param time: times for which the samples are to be got. All 126 if unspecified 127 @param note: A short annotation (for plots) 128 @param scale: pair of factors with which the data is scaled when being plotted 129 @param offset: pair of offsets""" 130 131 if line==None: 132 line=self.lines() 133 if value==None: 134 value=list(self.values()) 135 if time==None: 136 time=self.times 137 138 sets=[] 139 140 for t in time: 141 for l in line: 142 for v in value: 143 try: 144 d=self[t][(l,v)] 145 if d==None: 146 continue 147 d.note=note 148 d.scale=scale 149 d.offset=offset 150 sets.append(d) 151 except KeyError: 152 pass 153 154 return sets
155
156 -class SampleTime(object):
157 """A directory with one sampled time""" 158
159 - def __init__(self, 160 sDir, 161 time, 162 postfixes=[], 163 prefixes=[], 164 valueNames=None, 165 namesFromFirstLine=False, 166 linePattern=None, 167 needsExtension=True):
168 """@param sDir: The sample-dir 169 @param time: the timename 170 @param postfixes: list of possible extensions to a field name of the form 171 name_postfix to help splitting such field names. 172 @param prefixes: list of possible extensions to a field name of the form 173 prefix_name to help splitting such field names""" 174 175 self.dir=path.join(sDir,time) 176 self.lines=[] 177 self.values=[] 178 179 self.prefixes=prefixes 180 self.postfixes=postfixes 181 182 self.__valueNames=None 183 self.__defaultValueNames=valueNames 184 self.__linePattern=linePattern 185 self.__namesFromFirstLine=namesFromFirstLine 186 187 for f in listdir(self.dir): 188 if f[0]=='.' or f[-1]=='~' or (f.find(".")<0 and needsExtension): 189 continue 190 nm=self.extractLine(f) 191 if nm==None: 192 continue 193 vals=self.extractValues(f) 194 if nm not in self.lines: 195 self.lines.append(nm) 196 for v in vals: 197 if v not in self.values: 198 self.values.append(v) 199 200 self.lines.sort() 201 self.values.sort() 202 203 self.cache={}
204
205 - def extractLine(self,fName):
206 """Extract the name of the line from a filename""" 207 if self.__linePattern: 208 expr=re.compile(self.__linePattern) 209 try: 210 return expr.match(fName).groups(1)[0] 211 except AttributeError: 212 return None 213 else: 214 return fName.split("_")[0]
215
216 - def extractValues(self,fName):
217 """Extracts the names of the contained Values from a filename""" 218 219 if self.__defaultValueNames: 220 self.__valueNames=self.__defaultValueNames[:] 221 return self.__valueNames 222 223 if self.__namesFromFirstLine: 224 line=open(path.join(self.dir,fName)).readline().split() 225 if line[0]!="#": 226 error("First line of",path.join(self.dir,fName), 227 "does not start with a '#'") 228 return line[2:] 229 230 def preUnder(m): 231 return "&"+m.group(1)+m.group(2)
232 def postUnder(m): 233 return m.group(1)+m.group(2)+"&"
234 235 for p in self.prefixes: 236 fName=re.sub("([_&.]|^)("+p+")_",postUnder,fName) 237 for p in self.postfixes: 238 fName=re.sub("_("+p+")([_&.]|$)",preUnder,fName) 239 240 self.__valueNames=[] 241 try: 242 tmp=fName.split("_")[1:] 243 tmp[-1]=tmp[-1].split(".")[0] 244 245 for t in tmp: 246 self.__valueNames.append(t.replace("&","_")) 247 except IndexError: 248 pass 249 250 return self.__valueNames 251
252 - def __getitem__(self,key):
253 """Get the data for a value on a specific line 254 @param key: A tuple with the line-name and the value-name 255 @returns: A SampleData-object""" 256 257 if key in self.cache: 258 return self.cache[key] 259 260 line,val=key 261 if line not in self.lines or val not in self.values: 262 raise KeyError(key) 263 264 fName=None 265 266 for f in listdir(self.dir): 267 if line==self.extractLine(f) and val in self.extractValues(f): 268 fName=f 269 break 270 271 if fName==None: 272 error("Can't find a file for the line",line,"and the value",val,"in the directory",self.dir) 273 274 first=True 275 coord=[] 276 data=[] 277 index=None 278 279 for l in open(path.join(self.dir,fName)).readlines(): 280 if l.strip()[0]=='#': 281 continue 282 283 tmp=l.split() 284 if self.__defaultValueNames: 285 if len(tmp)!=len(self.__defaultValueNames)+1: 286 error("Number of items in line",l, 287 "is not consistent with predefined name", 288 self.__defaultValueNames) 289 if first: 290 first=False 291 vector,index=self.determineIndex(fName,val,tmp) 292 293 coord.append(float(tmp[0])) 294 try: 295 if vector: 296 data.append(tuple(map(float,tmp[index:index+3]))) 297 else: 298 data.append(float(tmp[index])) 299 except IndexError: 300 raise KeyError(key) 301 302 if index!=None: 303 self.cache[key]=SampleData(fName=path.join(self.dir,fName), 304 name=val, 305 line=self.extractLine(fName), 306 index=index, 307 coord=coord, 308 data=data) 309 310 return self.cache[key] 311 else: 312 return None
313
314 - def determineIndex(self,fName,vName,data):
315 """Determines the index of the data from the filename and a dataset 316 @param fName: name of the file 317 @param vName: Name of the quantity 318 @param data: A list with the data 319 @returns: A tuple of a boolean (whether the data is supposed to be 320 a vector or a scalar) and an integer (the index of the data set - 321 or the first component of the vector""" 322 323 vals=self.extractValues(fName) 324 325 if len(vals)+1==len(data): 326 vector=False 327 elif len(vals)*3+1==len(data): 328 vector=True 329 else: 330 error("The data in file",fName,"is neither vector nor scalar:",data) 331 332 index=vals.index(vName) 333 if vector: 334 index=index*3+1 335 else: 336 index=index+1 337 338 return vector,index
339
340 -class SampleData(object):
341 """Data from a sample-set""" 342
343 - def __init__(self, 344 fName, 345 name, 346 line, 347 index, 348 coord, 349 data, 350 note="", 351 scale=(1,1), 352 offset=(0,0)):
353 """@param fName: Name of the file 354 @param name: Name of the value 355 @param index: Index of the data in the file 356 @param coord: Values that identify the data (the location) 357 @param data: The actual data 358 @param scale: pair of factors with which the data is scaled when being plotted 359 @param offset: pair of offsets""" 360 361 self.file=fName 362 self.coord=coord 363 self.data=data 364 self.name=name 365 self.__line=line 366 self.index=index 367 self.note=note 368 self.scale=scale 369 self.offset=offset
370
371 - def __repr__(self):
372 if self.isVector(): 373 vect=" (vector)" 374 else: 375 vect="" 376 377 return "SampleData of %s%s on %s at t=%s " % (self.name,vect,self.line(),self.time())
378
379 - def line(self):
380 """Get the line of the sample""" 381 return self.__line
382
383 - def time(self):
384 """Get the time of the sample (as a string)""" 385 return path.basename(path.dirname(self.file))
386
387 - def isVector(self):
388 """Is this vector or scalar data?""" 389 if type(self.data[0])==tuple: 390 return True 391 else: 392 return False
393
394 - def range(self,component=None):
395 """Range of the data""" 396 data=self.component(component) 397 398 return (min(data),max(data))
399
400 - def domain(self):
401 """Range of the data domain""" 402 return (min(self.coord),max(self.coord))
403
404 - def component(self,component=None):
405 """Return the data as a number of single scalars. 406 @param component: If None for vectors the absolute value is taken. 407 else the number of the component""" 408 409 if self.isVector(): 410 data=[] 411 if component==None: 412 for d in self.data: 413 data.append(math.sqrt(d[0]*d[0]+d[1]*d[1]+d[2]*d[2])) 414 else: 415 if component<0 or component>=len(self.data[0]): 416 error("Requested component",component,"does not fit the size of the data",len(self.data[0])) 417 for d in self.data: 418 data.append(d[component]) 419 return data 420 else: 421 return self.data
422
423 - def __call__(self, 424 scaleX=1., 425 scaleData=1, 426 offsetData=0, 427 offsetX=0):
428 """Return the data as SpreadsheetData-object""" 429 430 data=[] 431 if self.isVector(): 432 for i,c in enumerate(self.coord): 433 data.append([scaleX*c+offsetX]+[scaleData*v+offsetData for v in self.data[i]]) 434 else: 435 for i,c in enumerate(self.coord): 436 data.append([scaleX*c+offsetX,scaleData*self.data[i]+offsetData]) 437 438 names=["coord"] 439 if self.isVector(): 440 names+=[self.name+"_x",self.name+"_y",self.name+"_z"] 441 else: 442 names.append(self.name) 443 444 return SpreadsheetData(data=data, 445 names=names, 446 title="%s_t=%s" % (self.line(),self.time()))
447 448 # Should work with Python3 and Python2 449