Created
April 27, 2011 10:45
-
-
Save jbeezley/944049 to your computer and use it in GitHub Desktop.
A python script to output vtk files from WRF-Fire output.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
''' | |
WRF2VTK.py | |
Jonathan Beezley | |
April 27, 2011 | |
This is a python script to convert WRF-Fire output files into a series | |
of vts (vtk structured grid) files. This script depends on the following | |
python modules: | |
vtk | |
netCDF4 | |
All variables are interpolated (linearly) to cell centered coordinates. At | |
most three files will be output at each time step. One for 3D atmospheric | |
arrays, and one for 2D atmospheric and fire grid surface arrays. All time | |
steps are numbered sequentially and a *.pvd file is created containing a | |
list of all files created as well as the simulation time created from the | |
DT attribute of the wrfout files. The pvd file can be used with paraview | |
to generate an animation. | |
Common usage: | |
python WRF2VTK.py -v FGRNHFX,GRNHFX,NFUEL_CAT -w surface_wind:UF:VF,atm_wind:U:V:W wrfout_d01* | |
Try `python WRF2VTK.py -h` for a complete list of options. | |
''' | |
from netCDF4 import Dataset | |
import numpy as np | |
import optparse | |
import vtk | |
from vtk.util import numpy_support | |
import sys | |
global current_output_time | |
current_output_time=0 | |
def getGridInfo(file,varname): | |
var=file.variables[varname] | |
dims=var.dimensions[1:] | |
if 'south_north_subgrid' in dims: | |
return 'fire' | |
elif len(dims) == 2: | |
return 'surface' | |
elif len(dims) == 3: | |
return 'atm' | |
else: | |
raise Exception('Unsupported variable type %s'%varname) | |
def getSRatio(f): | |
try: | |
srx=len(f.dimensions['west_east_subgrid'])/(len(f.dimensions['west_east'])+1) | |
sry=len(f.dimensions['south_north_subgrid'])/(len(f.dimensions['south_north'])+1) | |
except: | |
print >> sys.stderr, "WARNING: could not determine subgrid ratio, using 1." | |
srx=1 | |
sry=1 | |
return srx,sry | |
def readGrid(file,gridname,itime=0): | |
print 'Creating %s grid'%gridname | |
try: | |
dx=file.DX | |
dy=file.DY | |
except: | |
print >> sys.stderr, "Could not read spatial resolution, setting to 1." | |
dx=1 | |
dy=1 | |
if gridname == 'fire': | |
srx,sry=getSRatio(file) | |
dx=dx/float(srx) | |
dy=dy/float(sry) | |
nx=len(file.dimensions['west_east_subgrid'])-srx | |
ny=len(file.dimensions['south_north_subgrid'])-sry | |
else: | |
nx=len(file.dimensions['west_east']) | |
ny=len(file.dimensions['south_north']) | |
x=(np.arange(nx,dtype=np.float32)+.5)*dx | |
y=(np.arange(ny,dtype=np.float32)+.5)*dy | |
X,Y=np.meshgrid(x,y) | |
X.shape=(1,)+X.shape | |
Y.shape=(1,)+Y.shape | |
X=transposeVar(X) | |
Y=transposeVar(Y) | |
#print X.shape,Y.shape | |
if gridname == 'atm': | |
c=readFromNC(file,itime,('PH','PHB')) | |
ph=c['PH'] | |
phb=c['PHB'] | |
Z=(ph+phb)/9.81 | |
X=np.repeat(X,Z.shape[0],0) | |
Y=np.repeat(Y,Z.shape[0],0) | |
#print X.shape,Y.shape | |
else: | |
if gridname == 'fire': | |
Z=readFromNC(file,0,('ZSF',))['ZSF'] | |
else: | |
Z=readFromNC(file,0,('HGT',))['HGT'] | |
Z.shape=X.shape | |
return vectorize([X,Y,Z]) | |
def openNCFile(filename): | |
return Dataset(filename,'r') | |
def transposeVar(var): | |
#dims=np.arange(var.ndim-1,-1,-1) | |
return var#.transpose(dims) | |
def readFromNC(f,itime,vars): | |
out={} | |
for vname in vars: | |
print 'Reading %s at timestep %i'%(vname,itime) | |
var=f.variables[vname] | |
vardata=var[itime,:].squeeze() | |
if vardata.ndim != 2 and vardata.ndim != 3: | |
raise Exception('Only 2 or 3 dimensional variables are supported (%s).'%vname) | |
if 'west_east_stag' in var.dimensions: | |
vardata=0.5*(vardata[...,1:]+vardata[...,:-1]) | |
if 'south_north_stag' in var.dimensions: | |
vardata=0.5*(vardata[...,1:,:]+vardata[...,:-1,:]) | |
if 'bottom_top_stag' in var.dimensions: | |
vardata=0.5*(vardata[...,1:,:,:]+vardata[...,:-1,:,:]) | |
if 'south_north_subgrid' in var.dimensions: | |
if not 'west_east_subgrid' in var.dimensions or vardata.ndim != 2: | |
raise Exception('Invalid fire grid variable (%s).'%vname) | |
srx,sry=getSRatio(f) | |
#print srx,sry,vardata.shape | |
vardata=vardata[...,:-sry,:-srx] | |
#print vardata.shape | |
vardata=transposeVar(vardata) | |
if vardata.ndim == 2: | |
vardata.shape=(1,)+vardata.shape | |
out[vname]=vardata | |
return out | |
def vectorize(vars): | |
if len(vars) == 2 and vars[0].shape == vars[1].shape: | |
vars.append(np.zeros(vars[0].shape)) | |
elif len(vars) == 3 and vars[0].shape == vars[1].shape == vars[2].shape: | |
pass | |
else: | |
print vars[0].shape,vars[1].shape#,vars[2].shape | |
raise Exception('Invalid vector.') | |
v=np.ndarray(vars[0].shape+(3,),dtype=vars[0].dtype) | |
v[...,0]=vars[0] | |
v[...,1]=vars[1] | |
v[...,2]=vars[2] | |
return v | |
def parseVectors(vectors): | |
if isinstance(vectors,str): | |
vectors=vectors.split(',') | |
out={} | |
for v in vectors: | |
vec=v.strip().split(':') | |
out[vec[0].strip()]=[w.strip() for w in vec[1:]] | |
return out | |
def parseVariables(variables): | |
if isinstance(variables,str): | |
variables=variables.split(',') | |
return [v.strip() for v in variables] | |
def parseFiles(files): | |
if isinstance(files,str): | |
files=files.split(',') | |
return [f.strip() for f in files] | |
def numpy_to_vtk(vdata,vname=None,vector=False): | |
#print 'Converting %s to vtk array'%vname | |
shp=vdata.shape | |
if not vdata.flags['C_CONTIGUOUS']: | |
deep=1 | |
else: | |
deep=0 | |
#print 'deep=%i'%deep | |
vdata=np.ascontiguousarray(vdata,dtype=vdata.dtype) | |
if vector: | |
vdata.shape=vdata.size/3,3 | |
else: | |
vdata.shape=vdata.size | |
vtkdata=numpy_support.numpy_to_vtk(vdata, deep=deep) | |
if vname: | |
vtkdata.SetName(vname) | |
vdata.shape=shp | |
return vtkdata | |
def getVTKsgrid(grid): | |
shape=[i for i in reversed(grid.shape[:-1])] | |
print 'Creating vtk structured grid with shape %s'%str(shape) | |
pts=getVTKpoints(grid) | |
sgrid=vtk.vtkStructuredGrid() #@UndefinedVariable | |
sgrid.SetPoints(pts) | |
sgrid.SetExtent(0,shape[0]-1,0,shape[1]-1,0,shape[2]-1) | |
return sgrid | |
def addVTKvariable(sgrid,varname,vardata,vector=False): | |
shape=[i for i in reversed(vardata.shape[:3])] | |
if len(vardata.shape) == 4: | |
shape.append(vardata.shape[3]) | |
print 'Adding variable %s with shape %s'%(varname,str(shape)) | |
vdata=numpy_to_vtk(vardata,varname,vector) | |
sgrid.GetPointData().AddArray(vdata) | |
def getVTKpoints(grid): | |
shape=grid.shape | |
#nlen=np.prod(shape[:-1]) | |
#grid.shape=(nlen,shape[-1]) | |
vgrid=numpy_to_vtk(grid,vector=True) | |
pts=vtk.vtkPoints() #@UndefinedVariable | |
pts.SetData(vgrid) | |
return pts | |
def writeVTKsgrid(filename,sgrid,compress=True): | |
w=vtk.vtkXMLStructuredGridWriter() #@UndefinedVariable | |
print 'Writing VTK file %s'%filename | |
w.SetFileName(filename) | |
w.SetInput(sgrid) | |
if compress: | |
w.SetCompressorTypeToZLib() | |
else: | |
w.SetCompressorTypeToNone() | |
w.Update() | |
w.Write() | |
def writeVTK(grids,variables,vectors,vardata,output,filefmt,nocompress,**kwdargs): | |
global current_output_time | |
current_output_time=current_output_time+1 | |
files={} | |
for gridname in grids: | |
g,vars=grids[gridname] | |
sgrid=getVTKsgrid(g) | |
for vname in set(vars).intersection(variables): | |
addVTKvariable(sgrid,vname,vardata[vname]) | |
for vecname in vectors: | |
vector=vectors[vecname] | |
if vector and vector[0] in vars: | |
if not all([(iv in vars) for iv in vector]): | |
raise Exception('Not all variables in vector %s are on the same grid.'%vecname) | |
vecdata=vectorize([vardata[v] for v in vector]) | |
addVTKvariable(sgrid,vecname,vecdata,vector=True) | |
files[gridname]=filefmt%{'grid':gridname,'time':current_output_time}+'.vts' | |
writeVTKsgrid(files[gridname],sgrid,not nocompress) | |
return files | |
def writePVD(filename,files,dt=1,compression=True): | |
outfile=open(filename,'w') | |
if sys.byteorder == 'little': | |
byteorder='LittleEndian' | |
else: | |
byteorder='BigEndian' | |
if compression: | |
compressor=' compressor="vtkZLibDataCompressor"' | |
else: | |
compressor='' | |
header='<?xml version="1.0"?>\n'+ \ | |
'<VTKFile type="Collection" version="0.1" byte_order="%s"%s>\n' % (byteorder,compressor) + \ | |
'<Collection>\n' | |
footer='</Collection>\n'+'</VTKFile>' | |
filefm='<DataSet timestep="%f" group="" part="" file="%s"/>\n' | |
outfile.write(header) | |
print 'Creating group file %s'%filename | |
for i in xrange(len(files)): | |
outfile.write(filefm % (i*dt,files[i])) | |
outfile.write(footer) | |
def main(files,variables,vectors,time=-1,nocompress=False,output='wrfout'): | |
time=int(time) | |
variables=parseVariables(variables) | |
vectors=parseVectors(vectors) | |
files=parseFiles(files) | |
ofiles=[] | |
atime=time | |
otime=0 | |
for f in files: | |
print 'Reading from %s'%f | |
file=openNCFile(f) | |
if time == -1: | |
time = np.arange(len(file.dimensions['Time'])) | |
else: | |
time=[time] | |
allvars=sum(vectors.values(),[]) | |
allvars=set(allvars).union(variables) | |
for t in time: | |
grids={} | |
for var in allvars: | |
gridname=getGridInfo(file,var) | |
try: | |
g=grids[gridname] | |
except KeyError: | |
g=(readGrid(file,gridname,t),[]) | |
grids[gridname]=g | |
g[1].append(var) | |
vardata=readFromNC(file,t,allvars) | |
otime=otime+1 | |
ofile=writeVTK(variables=variables,vardata=vardata,vectors=vectors, | |
output=output,filefmt='%(grid)s_%(time)05i', | |
nocompress=nocompress,grids=grids,itime=otime) | |
ofiles.append(ofile) | |
time=atime | |
gridnames=ofiles[0].keys() | |
try: | |
dt=file.DT | |
except: | |
dt=1 | |
for n in gridnames: | |
writePVD('%s.pvd'%n,[ofiles[i][n] for i in xrange(len(ofiles))], | |
compression=not nocompress,dt=dt) | |
if __name__ == '__main__': | |
parser=optparse.OptionParser(usage='usage: %prog [options] filename') | |
parser.add_option('-v','--variables',action="store",type="string",dest='variables', | |
default='',help='A comma separated list of variables') | |
parser.add_option('-w','--vectors',action='store',type='string',dest='vectors', | |
default='',help='A comma separated list of vectors (i.e. -w wind1:U:V:W,wind2:UF:VF)') | |
parser.add_option('-t','--time',action="store",type="string",dest="time", | |
default="-1",help='The time slice to convert [default convert all time steps]') | |
parser.add_option('-n','--nocompress',action='store_true',dest='nocompress', | |
default=False,help='Do not save as a compressed file') | |
#parser.add_option('-s','--scale',action="store",type="float",dest="scale", | |
# default=scale_degree,help="vertical to horizontal scaling factor") | |
(options,args)= parser.parse_args() | |
if len(args) < 1: | |
parser.print_help() | |
raise Exception('NetCDF file name required.') | |
opt={'variables':options.variables,'vectors':options.vectors, | |
'time':options.time,'nocompress':options.nocompress} | |
main(args,**opt) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment