https://github.com/ElsevierSoftwareX/SOFTX-D-18-00002
Tip revision: 3210d601750edc3090b5d55686a2e7cda3497a3b authored by Alastair Basden on 04 June 2018, 16:50:50 UTC
debug
debug
Tip revision: 3210d60
saveOutput.py
#dasp, the Durham Adaptive optics Simulation Platform.
#Copyright (C) 2004-2016 Alastair Basden and Durham University.
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU Affero General Public License as
#published by the Free Software Foundation, either version 3 of the
#License, or (at your option) any later version.
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#GNU Affero General Public License for more details.
#You should have received a copy of the GNU Affero General Public License
#along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
import types,os
import numpy
import cmod.utils
import base.aobase
import util.FITS
class saveOutput(base.aobase.aobase):
"""
A class for saving the output to a FITS file. Should you wish to save only part of the output, or
the output of several different objects, use splitOutput or joinOutput first.
@cvar parent: the parent
@type parent: object
@cvar config: config object
@type config: object
@cvar args: dict of arguements
@type args: dict
@cvar shape: Shape of the output array
@type shape: Tuple of Ints
@cvar dtype: Datatype of output array
@type dtype: Char
@cvar debug: Flag, whether to print debug message (if None, won't print)
@type debug: None or user defined.
"""
def __init__(self,parent,config,args={},forGUISetup=0,debug=None,idstr=None):
"""Initialise the splitOutput module.
@param parent: Parent object which generates the data
@type parent: Parent object
@param config: Configuration object
@type config: Object
@param args: Dictionary of arguments, at present can contain keys: idstr
@type args: Dict
@param forGUISetup: whether for GUI or not
@type forGUISetup: Int
@param debug: Flag, whether to print debug message (if None, won't print)
@type debug: None or user defined.
"""
base.aobase.aobase.__init__(self,parent,config,args,forGUISetup=forGUISetup,debug=debug,idstr=idstr)
self.ended=0
self.initialised=0
self.finalised=0
self.outputData=None
self.parentList=None
self.forGUISetup=forGUISetup
if args.has_key("filename"):
self.filename=args["filename"]
else:
if len(self.idstr)==0 or self.idstr[0]==None:
ids=""
else:
ids="_%s"%self.idstr[0]
self.filename=self.config.getVal("saveOutputFilename",default="saveOutput%sb%d.fits"%(ids,config.batchno))
print("INFORMATION:**saveOutput:{:s}**: filename is '{:s}'".format(
str(self.idstr), self.filename )
)
self.doByteSwap=self.config.getVal("doByteSwap",default=0)
self.dataValid=0#never changes... no output...
if forGUISetup:
self.outputData=[None,None]
def __del__(self):
self.endSim()
def endSim(self):
if self.ended==0:
self.ended=1
if self.finalised==0:
self.finalised=1
print(("INFORMATION:**saveOutput:{:s}**: Finalising FITS file"+
" {:s}").format( str(self.idstr), self.filename )
)
import stat
try:
s=os.stat(self.filename)
size=s[stat.ST_SIZE]
except:
print(("ERROR:**saveOutput:{:s}**: unable to stat file"+
" {:s}").format( str(self.idstr), self.filename )
)
size=0
#except:
# size=0
tmp=size%2880
if tmp>0:
try:
os.ftruncate(self.ff.fileno(),size+2880-tmp)
except:
print(("ERROR:**saveOutput:{:s}**: unable to "+
" finalize {:s}").format(
str(self.idstr), self.filename )
)
self.ff.close()
def finalInitialisation(self):
"""once we've got all the info we need, initialise here..."""
if self.initialised==0:
self.initialised=1
if type(self.parent)==type({}):
size=0
dtype="1"
isize=1
self.parentList=self.parent.keys()
self.parentList.sort()
for p in self.parentList:
od=self.parent[p].outputData
size+=od.size
if od.itemsize>isize:
dtype=od.dtype.char
isize=od.itemsize
elif od.itemsize==isize:
if od.dtype.char in ["f","F","d","D"]:
dtype=od.dtype.char
isize=od.itemsize
self.shape=(size,)
self.dtype=dtype
else:
self.shape=self.parent.outputData.shape
self.dtype=self.parent.outputData.dtype
print(self.idstr,self.parent)
if type(self.dtype)!=type(""):
self.dtype=self.dtype.char
if self.dtype=="f":
self.bitpix=-32
elif self.dtype=="d":
self.bitpix=-64
elif self.dtype=="i":
self.bitpix=32
elif self.dtype=="s":
self.bitpix=16
elif self.dtype=="1":
self.bitpix=8
else:
raise Exception("base.saveOutput - bitpix not known (please update)")
try:
self.ff=open(self.filename,"wb+",buffering=4096)
except:#there seemed to be a problem when the python code was called on a ext3 mount from a directory in a cifs mount... no idea why! The solution is to copy files to where you run them from...
print(("WARNING:**saveOutput:{:s}**: "+
"Unable to open file with buffering - trying without"
).format( str(self.idstr) )
)
try:
self.ff=open(self.filename,"wb+")
except:
print(("WARNING:**saveOutput:{:s}**: "+
"Unable to open file in wb+ mode. Trying w+."
).format( str(self.idstr) )
)
try:
self.ff=open(self.filename,"w+")
except:
print(("WARNING:**saveOutput:{:s}**: "+
"Unable to open file in w+ mode. Trying w."
).format( str(self.idstr) )
)
self.ff=open(self.filename,"w")
#now write the header...
self.nd=len(self.shape)+1
self.dims=list(self.shape[:])
self.dims.reverse()
self.dims+=[0]
util.FITS.WriteKey(self.ff,"SIMPLE","T")
util.FITS.WriteKey(self.ff,"BITPIX",self.bitpix)
util.FITS.WriteKey(self.ff,"NAXIS",str(self.nd))
for i in range(self.nd):
util.FITS.WriteKey(self.ff,"NAXIS%d"%(i+1),str(self.dims[i]))
self.axisIncPos=self.ff.tell()-80
util.FITS.WriteKey(self.ff,"EXTEND","T")
le=numpy.little_endian and self.doByteSwap==0
util.FITS.WriteKey(self.ff,"LITTLE_E",le,"whether file is little endian")
if le:
util.FITS.WriteKey(self.ff,"UNORDERD","T")
util.FITS.WriteComment(self.ff,"Note, this file is be non-FITS complient, saved on a little endian machine. All the data will be byteswapped.")
if self.parentList!=None:
util.FITS.WriteKey(self.ff,"PARENTS",str(self.parentList))
util.FITS.EndHeader(self.ff)
def newParent(self,parent,idstr=None):
self.parent=parent
self.initialised=0
def generateNext(self,msg=None):
"""called by the child when it wants more data.
@param msg: The message to pass to the parent (predecessor) object
@type msg: None or fwdMsg object
"""
if self.debug!=None:
print(("INFORMATION::**saveOutput:{:s}**: generateNext "+
"(debug={:s})").format(
str(self.idstr), str(self.debug) )
)
if self.generate==1:
if self.newDataWaiting:
if type(self.parent)==type({}):
dv=1
for p in self.parentList:
pp=self.parent[p]
dv&=pp.dataValid
if dv==1:#all data valid...
offset=0
for p in self.parentList:
pp=self.parent[p]
outputData=pp.outputData
if outputData.size+offset>self.shape[0]:
raise Exception("Combined output data too large... (with parent %s, becomes %d which is >%s)"%(p,outputData.size+offset,str(self.shape)))
if outputData.dtype.char!=self.dtype:
outputData=outputData.astype(self.outputData)
print(("WARNING::**saveOutput:{:s}**:"+
"converting outputData[{:s}] to type {:s}"
).format(
str(self.idstr), p, self.dtype)
)
if self.doByteSwap and numpy.little_endian:
outputData=outputData.byteswap()
self.ff.seek(0,2)#move to end of file.
self.ff.write(outputData)
offset+=outputData.size
if offset!=self.shape[0]:
raise Exception("Combined output data wrong shape (%d != %s)"%(offset,str(self.shape)))
self.ff.seek(self.axisIncPos)
key=self.ff.read(80)
self.ff.seek(self.axisIncPos)
newdim=int(key[10:])+1
util.FITS.WriteKey(self.ff,"NAXIS%d"%self.nd,str(newdim))
else:
print("saveOutput: Not all input data valid")
self.dataValid=0
else:
if self.parent.dataValid==1:
outputData=self.parent.outputData
if outputData.shape!=self.shape:
print("ERROR: saveOutput - outputdata not same as that given %s %s"%(str(outputData.shape),str(self.shape)))
raise Exception("Wrong shape in splitOutput")
if outputData.dtype.char!=self.dtype:
outputData=outputData.astype(self.dtype)
print("Warning: saveOutput - converting outputData to type %s"%self.dtype)
if self.doByteSwap and numpy.little_endian:
outputData=outputData.byteswap()
self.ff.seek(0,2)#move to end of file.
self.ff.write(outputData)
self.ff.seek(self.axisIncPos)
key=self.ff.read(80)
self.ff.seek(self.axisIncPos)
newdim=int(key[10:])+1
util.FITS.WriteKey(self.ff,"NAXIS%d"%self.nd,str(newdim))
elif self.debug!=None:
print(("INFORMATION::**saveOutput:{:s}**: waiting for "+
"data but not valid (debug={:s})").format(
str(self.idstr), self.debug )
)
self.dataValid=0
else:
self.dataValid=0
if __name__=="__main__":
class dummy:
outputData=numpy.zeros((5,10),"i")
dataValid=1
class dumconfig:
rank,batchno=0,0
def __init__(self):
pass
def getVal(self,val,default=None,raiseerror=1):
return "tmp.fits"
def setSearchOrder(self,so):
pass
parent=dummy()
config=dumconfig()
so=saveOutput(parent,config)
so.finalInitialisation()
for i in range(14):
parent.outputData[:]=i
so.generateNext()