Source code for PyFoam.Basics.DataStructures

"""Data structures in Foam-Files that can't be directly represented by Python-Structures"""

from __future__ import division

from copy import deepcopy
import math
import re

# import FoamFileGenerator in the end to avoid circular dependencies

from PyFoam.ThirdParty.six import integer_types,PY3,string_types,StringIO

if PY3:
[docs] def cmp(a,b): if a<b: return -1 elif a==b: return 0 else: return 1
[docs]class FoamDataType(object): def __repr__(self): return "'"+str(self)+"'" def __eq__(self,other): """Implementation to make __cmp__ work again in Python3 Implementing this method means that these objects are not hashable. But that is OK """ return self.__cmp__(other)==0 def __lt__(self,other): "Implementation to make __cmp__ work again in Python3" return self.__cmp__(other)<0 def __ne__(self,other): return self.__cmp__(other)!=0 def __gt__(self,other): return self.__cmp__(other)>0 def __ge__(self,other): return self.__cmp__(other)>=0 def __le__(self,other): return self.__cmp__(other)<=0
[docs]class Field(FoamDataType): def __init__(self,val,name=None,length=None): self.val=val self.name=name self.length=length if type(val) in[list,UnparsedList,BinaryList]: self.uniform=False elif self.name==None: self.uniform=True else: raise TypeError("Type",type(val),"of value",val,"can not be used to determine uniformity") if self.length: if not self.uniform: raise TypeError("Type",type(val),"can't be used with a uniform field (length ",length," specified)") def __str__(self): result="" if self.length: result+=str(self.length)+" {" result+=str(self.val)+"}" else: if self.uniform: result+="uniform " else: result+="nonuniform " if self.name: result+=self.name+" " result+=str( PyFoam.Basics.FoamFileGenerator.FoamFileGenerator( self.val, longListThreshold=-1, useFixedType=False )) return result def __cmp__(self,other): if other is None or type(other)!=Field: return 1 if self.uniform!=other.uniform: return cmp(self.uniform,other.uniform) elif self.name!=other.name: return cmp(self.name,other.name) elif self.length!=other.length: return cmp(self.length,other.length) else: return cmp(self.val,other.val) def __getitem__(self,key): if self.length: if key>=0 and key<self.length: return self.val else: raise IndexError('Key',key,'outside of range 0 to',self.length-1) if not self.uniform: return self.val[key] else: return self.val def __setitem__(self,key,value): assert(not self.uniform) self.val[key]=value def __len__(self): if self.length: return self.length elif isinstance(self.val,(list,)): return len(self.val) else: raise TypeError("Operation len() unsupported for data of type",type(self.val))
[docs] def isUniform(self): return self.uniform
[docs] def isBinary(self): return type(self.val)==BinaryList
[docs] def binaryString(self): return "nonuniform "+self.name+" <BINARY DATA>"
[docs] def value(self): return self.val
[docs] def setUniform(self,data): self.val=data self.uniform=True self.name=None
[docs] def toNumpy(self,regexp,dtypes): """Convert to numpy-structured array (with one entry) @param regexp: Ignored. Just for compatibility with Unparsed @param dtypes: lsit of data types""" import numpy as np if self.length: try: tup=tuple(self.val) except TypeError: tup=(self.val,) result=np.repeat(np.array([tup],dtype=dtypes),self.length) return result else: raise TypeError("Can not convert",str(self),"to a numpy-array")
[docs]class Dimension(FoamDataType): def __init__(self,*dims): if len(dims)==1: self.dims=None self.dimString=dims[0] else: assert(len(dims)==7) self.dims=list(dims) def __str__(self): result="[ " if self.dims is None: result+=self.dimString+" " else: for v in self.dims: result+=str(v)+" " result+="]" return result def __cmp__(self,other): if other is None: return 1 if self.dims is None: if not hasattr(other,"dims"): return -1 if other.dims is not None: return -1 else: return cmp(self.dimString,other.dimString) else: if not hasattr(other,"dims"): return 1 if other.dims is None: return 1 else: return cmp(self.dims,other.dims) def __getitem__(self,key): return self.dims[key] def __setitem__(self,key,value): self.dims[key]=value
[docs]class FixedLength(FoamDataType): def __init__(self,vals): self.vals=vals[:] def __str__(self): return "("+" ".join(["%g"%v for v in self.vals])+")" def __cmp__(self,other): if other==None or not issubclass(type(other),FixedLength): return 1 return cmp(self.vals,other.vals) def __getitem__(self,key): return self.vals[key] def __setitem__(self,key,value): self.vals[key]=value def __len__(self): return len(self.vals)
[docs]class Vector(FixedLength): def __init__(self,x,y,z): FixedLength.__init__(self,[x,y,z]) def __add__(self,y): x=self if type(y)==Vector: return Vector(x[0]+y[0],x[1]+y[1],x[2]+y[2]) elif type(y) in integer_types+(float,): return Vector(x[0]+y,x[1]+y,x[2]+y) else: return NotImplemented def __radd__(self,y): x=self if type(y) in integer_types+(float,): return Vector(x[0]+y,x[1]+y,x[2]+y) else: return NotImplemented def __sub__(self,y): x=self if type(y)==Vector: return Vector(x[0]-y[0],x[1]-y[1],x[2]-y[2]) elif type(y) in integer_types+(float,): return Vector(x[0]-y,x[1]-y,x[2]-y) else: return NotImplemented def __rsub__(self,y): x=self if type(y) in integer_types+(float,): return Vector(y-x[0],y-x[1],y-x[2]) else: return NotImplemented def __mul__(self,y): x=self if type(y)==Vector: return Vector(x[0]*y[0],x[1]*y[1],x[2]*y[2]) elif type(y) in integer_types+(float,): return Vector(x[0]*y,x[1]*y,x[2]*y) else: return NotImplemented def __rmul__(self,y): x=self if type(y) in integer_types+(float,): return Vector(y*x[0],y*x[1],y*x[2]) else: return NotImplemented def __div__(self,y): x=self if type(y)==Vector: return Vector(x[0]/y[0],x[1]/y[1],x[2]/y[2]) elif type(y) in integer_types+(float,): return Vector(x[0]/y,x[1]/y,x[2]/y) else: return NotImplemented def __truediv__(self,y): return self.__div__(y) def __xor__(self,y): x=self if type(y)==Vector: return Vector(x[1]*y[2]-x[2]*y[1], x[2]*y[0]-x[0]*y[2], x[0]*y[1]-x[1]*y[0]) else: return NotImplemented def __abs__(self): x=self return math.sqrt(x[0]*x[0]+x[1]*x[1]+x[2]*x[2]) def __neg__(self): x=self return Vector(-x[0],-x[1],-x[2]) def __pos__(self): x=self return Vector( x[0], x[1], x[2])
[docs]class Tensor(FixedLength): def __init__(self,v1,v2,v3,v4,v5,v6,v7,v8,v9): FixedLength.__init__(self,[v1,v2,v3,v4,v5,v6,v7,v8,v9])
[docs]class SymmTensor(FixedLength): def __init__(self,v1,v2,v3,v4,v5,v6): FixedLength.__init__(self,[v1,v2,v3,v4,v5,v6])
[docs]class BoolProxy(object): """Wraps a boolean parsed from a file. Optionally stores a textual representation """ TrueStrings=["on", "yes", "true", # "y" # this breaks parsing certain files ] FalseStrings=[ "off", "no", "false", # "n", # this breaks parsing certain files # "none", # this breaks parsing of cases where the word none is needed "invalid" ] def __init__(self,val=None,textual=None): if val==None and textual==None: raise TypeError("'BoolProxy' initialized without values") elif val==None: if textual in BoolProxy.TrueStrings: self.val=True elif textual in BoolProxy.FalseStrings: self.val=False else: raise TypeError(str(textual)+" not in "+str(BoolProxy.TrueStrings) +" or "+str(BoolProxy.TrueStrings)) else: if val not in [True,False]: raise TypeError(str(val)+" is not a boolean") self.val=val self.textual=textual if self.textual: if self.val: if self.textual not in BoolProxy.TrueStrings: raise TypeError(self.textual+" not in " +str(BoolProxy.TrueStrings)) else: if self.textual not in BoolProxy.FalseStrings: raise TypeError(self.textual+" not in " +str(BoolProxy.FalseStrings)) def __nonzero__(self): return self.val # for Python 3 def __bool__(self): return self.val def __str__(self): if self.textual==None: if self.val: return "yes" else: return "no" else: return self.textual def __repr__(self): return self.__str__() def __eq__(self,o): if type(o) in [bool,BoolProxy]: return self.val==o elif isinstance(o,string_types): if self.textual==o: return True else: try: return self.val==BoolProxy(textual=o) except TypeError: return False else: # raise TypeError("Can't compare BoolProxy with "+str(type(o))) return self.val==o def __ne__(self,o): return not self.__eq__(o)
[docs]class DictRedirection(object): """This class is in charge of handling redirections to other directories""" def __init__(self,fullCopy,reference,name): self._fullCopy=fullCopy self._reference=reference self._name=name
[docs] def useAsRedirect(self): self._fullCopy=None
[docs] def getContent(self): result=self._fullCopy self._fullCopy=None return result
def __call__(self): return self._reference def __str__(self): return "$"+self._name def __float__(self): return float(self._reference)
[docs] def keys(self): if self._fullCopy: return self._fullCopy.keys() else: return self._reference.keys()
[docs]class DictProxy(dict): """A class that acts like a dictionary, but preserves the order of the entries. Used to beautify the output""" def __init__(self): dict.__init__(self) self._order=[] self._decoration={} self._regex=[] self._redirects=[]
[docs] def isRegexp(self,key): if type(key)==str: if key[0]=='"' and key[-1]=='"': return True return False
def __setitem__(self,key,value): if self.isRegexp(key): exp=re.compile(key[1:-1]) self._regex=[(key,exp,value)]+self._regex else: dict.__setitem__(self,key,value) if key not in self._order or self.isRegexp(key): self._order.append(key) def __getitem__(self,key): try: return dict.__getitem__(self,key) except KeyError: for k,e,v in self._regex: if e.match(key): return v for r in self._redirects: try: return r()[key] except KeyError: pass raise KeyError(key) def __delitem__(self,key): dict.__delitem__(self,key) self._order.remove(key) if key in self._decoration: del self._decoration[key] def __deepcopy__(self,memo): new=DictProxy() for k in self._order: if type(k)==DictRedirection: new.addRedirection(k) else: try: new[k]=deepcopy(self[k],memo) except KeyError: new[k]=deepcopy(self.getRegexpValue(k),memo) return new def __contains__(self,key): if dict.__contains__(self,key): return True else: for k,e,v in self._regex: if e.match(key): return True for r in self._redirects: if key in r(): return True return False def __enforceString(self,v,toString): if not isinstance(v,string_types) and toString: r=str(v) if isinstance(v,(list,dict)): r='"'+r+'"' return r else: return v
[docs] def update(self,other=None,toString=False,**kwargs): """Emulate the regular update of dict""" if other: if hasattr(other,"keys"): for k in other.keys(): self[k]=self.__enforceString(other[k],toString) else: for k,v in other: self[k]=self.__enforceString(v,toString) for k in kwargs: self[k]=self.__enforceString(kwargs[k],toString)
[docs] def keys(self): result=[x for x in self._order if x not in self._redirects and not self.isRegexp(x)] for r in self._redirects: for k in r.keys(): if not k in result: result.append(k) return result
def __iter__(self): s=set() for k in self._order: if k not in self._redirects and not self.isRegexp(k): s.add(k) yield k for r in self._redirects: for k in r.keys(): if not k in s: s.add(k) yield k def __str__(self): first=True result="{" for k in self.keys(): v=self[k] if first: first=False else: result+=", " result+="%s: %s" % (repr(k),repr(v)) result+="}" return result
[docs] def iteritems(self): lst=[] for k in self: lst.append((k,self[k])) return lst
# needed for python 3. Should be a generator, but ...
[docs] def items(self): return self.iteritems()
[docs] def addDecoration(self,key,text): if key in self: if key not in self._decoration: self._decoration[key]="" self._decoration[key]+=text
[docs] def getDecoration(self,key): if key in self._decoration: return " \t"+self._decoration[key] else: return ""
[docs] def getRegexpValue(self,key): for k,e,v in self._regex: if k==key: return v raise KeyError(key)
[docs] def addRedirection(self,redir): self._order.append(redir) redir.useAsRedirect() self._redirects.append(redir)
[docs]class TupleProxy(list): """Enables Tuples to be manipulated""" def __init__(self,tup=()): list.__init__(self,tup)
[docs]class Unparsed(object): """A class that encapsulates an unparsed string""" def __init__(self,data): self.data=data def __str__(self): return self.data def __hash__(self): return hash(self.data) def __lt__(self,other): return self.data<other.data
[docs] def toNumpy(self,regexp,dtypes): """Assume that the unparsed data contains line-wise data and transform it to a numpy-array. @param regexp: regular expression where the groups correspond to the dtypes, @param dtypes: list with dtypes""" import numpy as np try: return np.fromregex(StringIO(self.data), regexp, dtypes) except TypeError: from PyFoam.ThirdParty.six import BytesIO,b return np.fromregex(BytesIO(b(self.data)), regexp, dtypes)
[docs]class BinaryBlob(Unparsed): """Represents a part of the file with binary data in it""" def __init__(self,data): Unparsed.__init__(self,data)
[docs]class Codestream(str): """A class that encapsulates an codestream string""" def __str__(self): return "#{" + str.__str__(self) + "#}"
[docs]class UnparsedList(object): """A class that encapsulates a list that was not parsed for performance reasons""" def __init__(self,lngth,data): self.data=data self.length=lngth def __len__(self): return self.length def __cmp__(self,other): return cmp(self.data,other.data) def __eq__(self,other): return self.data==other.data def __lt__(self,other): return self.data<other.data
[docs] def toNumpy(self,regexp,dtypes): import numpy as np try: return np.fromregex(StringIO(self.data), regexp, dtypes) except TypeError: from PyFoam.ThirdParty.six import BytesIO,b return np.fromregex(BytesIO(b(self.data)), regexp, dtypes)
[docs]class BinaryList(UnparsedList): """A class that represents a list that is saved as binary data""" def __init__(self,lngth,data): UnparsedList.__init__(self,lngth,data)
[docs]def makePrimitiveString(val): """Make strings of types that might get written to a directory""" if isinstance(val,(Dimension,FixedLength,BoolProxy)): return str(val) else: return val
# Moved to the end to avoid circular dependencies import PyFoam.Basics.FoamFileGenerator # Should work with Python3 and Python2