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