source: nappy/trunk/naFile.py @ 2179

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/nappy/trunk/naFile.py@2179
Revision 2179, 10.3 KB checked in by domlowe, 12 years ago (diff)

Improved performance of two Nappy functions - readItemsFromUnknownLines and _checkForBlankLines

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1#   Copyright (C) 2004 CCLRC & NERC( Natural Environment Research Council ).
2#   This software may be distributed under the terms of the
3#   Q Public License, version 1.0 or later. http://ndg.nerc.ac.uk/public_docs/QPublic_license.txt
4
5"""
6naFile.py
7=========
8
9A containter module for the mixin base class NAFile that is subclassed
10for individual FFIs. Each FFI class is held in an individual file.
11
12"""
13
14# 08/05/04 updated by selatham for bug fixes and new write methods
15
16# Imports from python standard library
17import sys
18import time
19import re
20import StringIO
21
22# Imports from nappy package
23import naCore
24from textParser import *
25from naError import *
26import naToCdms
27
28class NAFile(naCore.NACore, naToCdms.NAToCdms):
29
30    """
31    NAFile class is a sub-class of NACore and NAToCdms mixin classes.
32    NAFile is also a mixin class and should not be called directly.
33   
34    NAFile holds all the methods are common to either all or more than
35    one NASA Ames FFI class. These methods set up the main read and write
36    functionality for the NASA Ames format.
37
38    When a sub-class of NAFile is called with a read ('r' - default) or
39    write ('w') mode the header in the file is automatically read or written.
40    However, the user must explicitly read or write the data section with a
41    call to the 'readData' or 'writeData' methods.
42
43    """
44
45    def __init__(self, filename, mode="r", naDict={}, spacer="    ", floatFormat="%8.3f"):
46        """
47        Initialization of class, decides if user wishes to read or write
48        NASA Ames file.
49        """
50        naCore.NACore.__init__(self)
51        self.naDict=naDict
52        self.filename=filename
53        self._open(mode)
54        self.spacer=spacer
55        self.floatFormat=floatFormat
56
57        if mode=="r":
58            self._normalizedX="yes"
59            self.readHeader()
60        elif mode=="w":
61            self._parseDictionary()
62            self.header=StringIO.StringIO()
63            self.writeHeader()
64            self.writeData()
65        else:
66            raise "Unknown file mode '%s'." % mode
67        self.close()
68       
69    def _open(self, mode):
70        "Wrapper to builtin open file function."
71        self.file=open(self.filename, mode)
72
73    def close(self):
74        "Wrapper to builtin close file function."
75        self.file.close()
76
77    def _parseDictionary(self):
78        """
79        Parser for the optional naDict argument containing a dictionary
80        of NASA Ames internal variables. These are saved as instance attributes
81        with the name used in the NASA Ames documentation.
82        """
83        for i in self.naDict.keys():
84            setattr(self, i, self.naDict[i])
85
86    def _readTopLine(self):
87        """
88        Reads number of header lines and File Format Index from top line.
89        Also assigns a value to NIV for the number of independent variables
90        based on the first character in the FFI.
91
92        Returns NLHEAD and FFI in a tuple.
93        """
94        (self.NLHEAD, self.FFI)=readItemsFromLine(self.file.readline(), 2, int)
95        self.NIV=int(self.FFI/1000)
96        return (self.NLHEAD, self.FFI)
97
98    def _readLines(self, nlines):
99        "Reads nlines lines from a file and returns them in a list."
100        lines=[]
101        for i in range(nlines):
102            lines.append(self.file.readline().strip())
103        return lines
104
105    def _checkForBlankLines(self, datalines):
106        """
107        Searches for empty lines in the middle of the data section and raises
108        as error if found. It ignores empty lines at the end of the file but
109        strips them out before returning a list of lines for reading.
110        """
111        empties=None
112        count=0
113        rtlines=[]
114        for line in datalines:
115            #if re.match(r"^[\s\n\t\r]*$", line):
116            if not line.strip(): #looks for blank line, quicker than reg ex
117                empties=1
118            else:
119                if empties==1:   # If data line found after empty line then raise
120                    raise "Empty line found in data section at line: %s" % count
121                else:
122                    rtlines.append(line)
123            count=count+1
124        return rtlines
125
126    def _readCommonHeader(self):
127        """
128        Reads the header section common to all NASA Ames files.
129        """
130        self._readTopLine()
131        self.ONAME=readItemFromLine(self.file.readline(), str)
132        self.ORG=readItemFromLine(self.file.readline(), str)
133        self.SNAME=readItemFromLine(self.file.readline(), str)
134        self.MNAME=readItemFromLine(self.file.readline(), str)
135        (self.IVOL, self.NVOL)=readItemsFromLine(self.file.readline(), 2, int)
136        dates=readItemsFromLine(self.file.readline(), 6, int)
137        (self.DATE, self.RDATE)=(dates[:3], dates[3:])
138
139    def _writeCommonHeader(self):
140        """
141        Writes the header section common to all NASA Ames files.
142        """
143        self.header.write("%s    %s\n" % (self.NLHEAD, self.FFI))
144        self.header.write("%s\n" % self.ONAME)
145        self.header.write("%s\n" % self.ORG)
146        self.header.write("%s\n" % self.SNAME)
147        self.header.write("%s\n" % self.MNAME)
148        self.header.write("%s    %s\n" % (self.IVOL, self.NVOL))
149        self.header.write("%s %s %s    %s %s %s\n" % (self.DATE[0], self.DATE[1], self.DATE[2], self.RDATE[0], self.RDATE[1], self.RDATE[2]))
150
151    def _readVariablesHeaderSection(self):
152        """
153        Reads the variables section of the header.
154        Assumes we are at the right point in the file.
155        """
156        self.NV=readItemFromLine(self.file.readline(), int)
157        self.VSCAL=readItemsFromUnknownLines(self.file, self.NV, float)
158        self.VMISS=readItemsFromUnknownLines(self.file, self.NV, float)
159        self.VNAME=readItemsFromLines(self._readLines(self.NV), self.NV, str)
160
161    def _writeVariablesHeaderSection(self):
162        """
163        Writes the variables section of the header.
164        Assumes we are at the right point in the file.
165        """       
166        self.header.write("%s\n" % self.NV)
167        self.header.write(("%s "*self.NV+"\n") % tuple(self.VSCAL))
168        self.header.write(("%s "*self.NV+"\n")  % tuple(self.VMISS))
169        self.header.write("%s\n"*self.NV % tuple(self.VNAME))
170
171    def _readAuxVariablesHeaderSection(self):
172        """
173        Reads the auxiliary variables section of the header.
174        Assumes we are at the right point in the file.
175        """
176        self.NAUXV=readItemFromLine(self.file.readline(), int)
177        if self.NAUXV>0:       
178            self.ASCAL=readItemsFromUnknownLines(self.file, self.NAUXV, float)
179            self.AMISS=readItemsFromUnknownLines(self.file, self.NAUXV, float)
180            self.ANAME=readItemsFromLines(self._readLines(self.NAUXV), self.NAUXV, str)
181
182    def _writeAuxVariablesHeaderSection(self):
183        """
184        Writes the auxiliary variables section of the header.
185        Assumes we are at the right point in the file.
186        """
187        self.header.write("%s\n" % self.NAUXV)
188        if self.NAUXV>0:
189            self.header.write(("%s "*self.NAUXV+"\n")  % tuple(self.ASCAL))
190            self.header.write(("%s "*self.NAUXV+"\n")  % tuple(self.AMISS))
191            self.header.write("%s\n"*self.NAUXV % tuple(self.ANAME))                    #08/11/04 selatham
192
193    def _readCharAuxVariablesHeaderSection(self):
194        """
195        Reads the character-encoded auxiliary variables section of the header.
196        Assumes we are at the right point in the file.
197        """
198        self.NAUXV=readItemFromLine(self.file.readline(), int)
199        self.NAUXC=readItemFromLine(self.file.readline(), int)
200        nonCharAuxVars=self.NAUXV-self.NAUXC
201        if self.NAUXV>0:
202            self.ASCAL=readItemsFromUnknownLines(self.file, nonCharAuxVars, float)
203            self.AMISS=readItemsFromUnknownLines(self.file, nonCharAuxVars, float)
204            self.LENA=readItemsFromUnknownLines(self.file, self.NAUXC, int)
205            for i in range(nonCharAuxVars):
206                self.LENA.insert(0, None)
207            self.AMISS=self.AMISS+readItemsFromUnknownLines(self.file, self.NAUXC, str)   
208            self.ANAME=readItemsFromLines(self._readLines(self.NAUXV), self.NAUXV, str)       
209           
210    def _readComments(self):
211        """
212        Reads the special and normal comments sections.
213        Assumes we are at the right point in the file.
214        """       
215        self.NSCOML=readItemFromLine(self.file.readline(), int)
216        self._readSpecialComments()
217        self.NNCOML=readItemFromLine(self.file.readline(), int)
218        self._readNormalComments()
219
220    def _writeComments(self):
221        """
222        Writes the special and normal comments sections.
223        Assumes we are at the right point in the file.
224        """ 
225        self.header.write("%s\n" % self.NSCOML)
226        self.header.write("%s\n"*self.NSCOML % tuple(self.SCOM))
227        self.header.write("%s\n" % self.NNCOML)
228        self.header.write("%s\n"*self.NNCOML % tuple(self.NCOM))
229
230    def _fixHeaderLength(self, header):
231        """
232        Takes the header StringIO object and counts the number of lines to
233        return a list of lines with the correct number of header lines in
234        the top line.
235        """
236        header.seek(0)
237        lines=header.readlines()
238        headlength=len(lines)
239        lines[0]="%s    %s\n" % (headlength, self.FFI)
240        return lines
241
242    def _readSpecialComments(self):
243        """
244        Reads the special comments section.       
245        Assumes that we are at the right point in the file and that NSCOML
246        variable is known.
247        """
248        self.SCOM=self._readLines(self.NSCOML)
249        return self.SCOM
250
251    def _readNormalComments(self):
252        """
253        Reads the normal comments section.       
254        Assumes that we are at the right point in the file and that NNCOML
255        variable is known.
256        """
257        self.NCOM=self._readLines(self.NNCOML)
258        return self.NCOM
259
260    def readData(self):
261        """
262        Reads the data section of the file. This method actually calls a number
263        of FFI specific methods to setup the data arrays (lists of lists) and
264        read the various data sections.
265
266        This method can be called directly by the user.
267        """
268        self._setupArrays()
269        datalines=open(self.filename).readlines()[self.NLHEAD:]
270        datalines=self._checkForBlankLines(datalines)
271
272        # Set up loop over unbounded indpendent variable
273        m=0   # Unbounded independent variable mark       
274        while len(datalines)>0:
275            datalines=self._readData1(datalines, m)
276            datalines=self._readData2(datalines, m)
277            m=m+1
278           
Note: See TracBrowser for help on using the repository browser.