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 """@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
42 - def __len__(self):
43 return len(self.times)
44
45 - def __iter__(self):
46 for t in self.times: 47 yield SampleTime(self.dir, 48 t, 49 prefixes=self.prefixes, 50 postfixes=self.postfixes)
51
52 - def __getitem__(self,time):
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
61 - def __contains__(self,time):
62 return time in self.times
63
64 - def lines(self):
65 """Returns all the found sample lines""" 66 67 lines=[] 68 69 for t in self: 70 for l in t.lines: 71 if l not in lines: 72 lines.append(l) 73 lines.sort() 74 75 return lines
76
77 - def values(self):
78 """Returns all the found sampled values""" 79 80 values=[] 81 82 for t in self: 83 for v in t.values: 84 if v not in values: 85 values.append(v) 86 values.sort() 87 88 return values
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
133 -class SampleTime(object):
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
173 - def extractLine(self,fName):
174 """Extract the name of the line from a filename""" 175 return fName.split("_")[0]
176
177 - def extractValues(self,fName):
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
202 - def __getitem__(self,key):
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
255 - def determineIndex(self,fName,vName,data):
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
280 -class SampleData(object):
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
309 - def __repr__(self):
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
317 - def line(self):
318 """Get the line of the sample""" 319 return path.basename(self.file).split("_")[0]
320
321 - def time(self):
322 """Get the time of the sample (as a string)""" 323 return path.basename(path.dirname(self.file))
324
325 - def isVector(self):
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):
333 """Range of the data""" 334 data=self.component(component) 335 336 return (min(data),max(data))
337
338 - def domain(self):
339 """Range of the data domain""" 340 return (min(self.col0),max(self.col0))
341
342 - def component(self,component=None):
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 # Should work with Python3 and Python2 387