source: trunk/anuga_core/source/anuga/file/sts.py @ 7778

Last change on this file since 7778 was 7778, checked in by hudson, 14 years ago

Cleaned up unit tests to use API.

File size: 11.8 KB
RevLine 
[7762]1import numpy as num
[7778]2import log
3from Scientific.IO.NetCDF import NetCDFFile
4
[7762]5from anuga.config import max_float
6from anuga.config import netcdf_float, netcdf_float32, netcdf_int
7from anuga.utilities.numerical_tools import ensure_numeric
8from anuga.coordinate_transforms.geo_reference import Geo_reference, \
9     ensure_geo_reference
[7778]10from anuga.config import netcdf_mode_r, netcdf_mode_w, netcdf_mode_a
[7760]11
12class Write_sts:
[7762]13    """ A class to write STS files.
14    """
15    sts_quantities = ['stage', 'xmomentum', 'ymomentum']
[7760]16    RANGE = '_range'
17    EXTREMA = ':extrema'
18
19    ##
20    # @brief Instantiate this STS writer class.
21    def __init__(self):
22        pass
23
24    ##
25    # @brief Write a header to the underlying data file.
26    # @param outfile Handle to open file to write.
27    # @param times A list of the time slice times *or* a start time.
28    # @param number_of_points The number of URS gauge sites.
29    # @param description Description string to write into the STS file.
30    # @param sts_precision Format of data to write (netcdf constant ONLY).
31    # @param verbose True if this function is to be verbose.
32    # @note If 'times' is a list, the info will be made relative.
33    def store_header(self,
34                     outfile,
35                     times,
36                     number_of_points,
37                     description='Converted from URS mux2 format',
38                     sts_precision=netcdf_float32,
39                     verbose=False):
40        """
41        outfile - the name of the file that will be written
42        times - A list of the time slice times OR a start time
43        Note, if a list is given the info will be made relative.
44        number_of_points - the number of urs gauges sites
45        """
46
47        outfile.institution = 'Geoscience Australia'
48        outfile.description = description
49
50        try:
51            revision_number = get_revision_number()
52        except:
53            revision_number = None
54
55        # Allow None to be stored as a string
56        outfile.revision_number = str(revision_number)
57
58        # Start time in seconds since the epoch (midnight 1/1/1970)
59        # This is being used to seperate one number from a list.
60        # what it is actually doing is sorting lists from numeric arrays.
61        if isinstance(times, (list, num.ndarray)):
62            number_of_times = len(times)
63            times = ensure_numeric(times)
64            if number_of_times == 0:
65                starttime = 0
66            else:
67                starttime = times[0]
68                times = times - starttime  #Store relative times
69        else:
70            number_of_times = 0
71            starttime = times
72
73        outfile.starttime = starttime
74
75        # Dimension definitions
76        outfile.createDimension('number_of_points', number_of_points)
77        outfile.createDimension('number_of_timesteps', number_of_times)
78        outfile.createDimension('numbers_in_range', 2)
79
80        # Variable definitions
81        outfile.createVariable('permutation', netcdf_int, ('number_of_points',))
82        outfile.createVariable('x', sts_precision, ('number_of_points',))
83        outfile.createVariable('y', sts_precision, ('number_of_points',))
[7762]84        outfile.createVariable('elevation', sts_precision, \
85                                    ('number_of_points',))
[7760]86
87        q = 'elevation'
88        outfile.createVariable(q + Write_sts.RANGE, sts_precision,
89                               ('numbers_in_range',))
90
91        # Initialise ranges with small and large sentinels.
92        # If this was in pure Python we could have used None sensibly
93        outfile.variables[q + Write_sts.RANGE][0] = max_float  # Min
94        outfile.variables[q + Write_sts.RANGE][1] = -max_float # Max
95
96        # Doing sts_precision instead of Float gives cast errors.
97        outfile.createVariable('time', netcdf_float, ('number_of_timesteps',))
98
99        for q in Write_sts.sts_quantities:
100            outfile.createVariable(q, sts_precision, ('number_of_timesteps',
101                                                      'number_of_points'))
102            outfile.createVariable(q + Write_sts.RANGE, sts_precision,
103                                   ('numbers_in_range',))
104            # Initialise ranges with small and large sentinels.
105            # If this was in pure Python we could have used None sensibly
106            outfile.variables[q + Write_sts.RANGE][0] = max_float  # Min
107            outfile.variables[q + Write_sts.RANGE][1] = -max_float # Max
108
109        if isinstance(times, (list, num.ndarray)):
110            outfile.variables['time'][:] = times    #Store time relative
111
112        if verbose:
113            log.critical('------------------------------------------------')
114            log.critical('Statistics:')
115            log.critical('    t in [%f, %f], len(t) == %d'
116                         % (num.min(times), num.max(times), len(times.flat)))
117
118    ##
119    # @brief
120    # @param outfile
121    # @param points_utm
122    # @param elevation
123    # @param zone
124    # @param new_origin
125    # @param points_georeference
126    # @param verbose True if this function is to be verbose.
127    def store_points(self,
128                     outfile,
129                     points_utm,
130                     elevation, zone=None, new_origin=None,
131                     points_georeference=None, verbose=False):
132
133        """
134        points_utm - currently a list or array of the points in UTM.
135        points_georeference - the georeference of the points_utm
136
137        How about passing new_origin and current_origin.
138        If you get both, do a convertion from the old to the new.
139
140        If you only get new_origin, the points are absolute,
141        convert to relative
142
143        if you only get the current_origin the points are relative, store
144        as relative.
145
146        if you get no georefs create a new georef based on the minimums of
147        points_utm.  (Another option would be to default to absolute)
148
149        Yes, and this is done in another part of the code.
150        Probably geospatial.
151
152        If you don't supply either geo_refs, then supply a zone. If not
153        the default zone will be used.
154
155        precondition:
156             header has been called.
157        """
158
159        number_of_points = len(points_utm)
160        points_utm = num.array(points_utm)
161
162        # given the two geo_refs and the points, do the stuff
163        # described in the method header
164        points_georeference = ensure_geo_reference(points_georeference)
165        new_origin = ensure_geo_reference(new_origin)
166
167        if new_origin is None and points_georeference is not None:
168            points = points_utm
169            geo_ref = points_georeference
170        else:
171            if new_origin is None:
172                new_origin = Geo_reference(zone, min(points_utm[:,0]),
173                                                 min(points_utm[:,1]))
174            points = new_origin.change_points_geo_ref(points_utm,
175                                                      points_georeference)
176            geo_ref = new_origin
177
178        # At this stage I need a georef and points
179        # the points are relative to the georef
180        geo_ref.write_NetCDF(outfile)
181
182        x = points[:,0]
183        y = points[:,1]
184        z = outfile.variables['elevation'][:]
185
186        if verbose:
187            log.critical('------------------------------------------------')
188            log.critical('More Statistics:')
189            log.critical('  Extent (/lon):')
190            log.critical('    x in [%f, %f], len(lat) == %d'
191                         % (min(x), max(x), len(x)))
192            log.critical('    y in [%f, %f], len(lon) == %d'
193                         % (min(y), max(y), len(y)))
194            log.critical('    z in [%f, %f], len(z) == %d'
195                         % (min(elevation), max(elevation), len(elevation)))
196            log.critical('geo_ref: %s' % str(geo_ref))
197            log.critical('------------------------------------------------')
198
199        z = resize(bath_grid,outfile.variables['elevation'][:].shape)
200        outfile.variables['x'][:] = points[:,0] #- geo_ref.get_xllcorner()
201        outfile.variables['y'][:] = points[:,1] #- geo_ref.get_yllcorner()
202        #outfile.variables['z'][:] = elevation
203        outfile.variables['elevation'][:] = elevation  #FIXME HACK4
204
205        # This updates the _range values
206        q = 'elevation'
207        outfile.variables[q + Write_sts.RANGE][0] = min(elevation)
208        outfile.variables[q + Write_sts.RANGE][1] = max(elevation)
209
210    ##
211    # @brief Store quantity data in the underlying file.
212    # @param outfile
213    # @param sts_precision
214    # @param slice_index
215    # @param time
216    # @param verboseTrue if this function is to be verbose.
217    # @param **quant Extra keyword args.
218    def store_quantities(self, outfile, sts_precision=num.float32,
219                         slice_index=None, time=None,
220                         verbose=False, **quant):
221        """Write the quantity info.
222
223        **quant is extra keyword arguments passed in. These must be
224          the sts quantities, currently; stage.
225
226        if the time array is already been built, use the slice_index
227        to specify the index.
228
229        Otherwise, use time to increase the time dimension
230
231        Maybe make this general, but the viewer assumes these quantities,
232        so maybe we don't want it general - unless the viewer is general
233
234        precondition:
235            triangulation and header have been called.
236        """
237
238        if time is not None:
239            file_time = outfile.variables['time']
240            slice_index = len(file_time)
241            file_time[slice_index] = time
242
243        # Write the conserved quantities from Domain.
244        # Typically stage, xmomentum, ymomentum
245        # other quantities will be ignored, silently.
246        # Also write the ranges: stage_range
247        for q in Write_sts.sts_quantities:
248            if not quant.has_key(q):
249                msg = 'STS file can not write quantity %s' % q
250                raise NewQuantity, msg
251            else:
252                q_values = quant[q]
253                outfile.variables[q][slice_index] = \
254                                q_values.astype(sts_precision)
255
256                # This updates the _range values
257                q_range = outfile.variables[q + Write_sts.RANGE][:]
258                q_values_min = num.min(q_values)
259                if q_values_min < q_range[0]:
260                    outfile.variables[q + Write_sts.RANGE][0] = q_values_min
261                q_values_max = num.max(q_values)
262                if q_values_max > q_range[1]:
263                    outfile.variables[q + Write_sts.RANGE][1] = q_values_max
264
265
266
267
268
269##
270# @brief Create a list of points defining a boundary from an STS file.
271# @param stsname Stem of path to the STS file to read.
272# @return A list of boundary points.
[7778]273def create_sts_boundary(sts_filename):
[7760]274    """Create a list of points defining a boundary from an STS file.
275
276    Create boundary segments from .sts file. Points can be stored in
277    arbitrary order within the .sts file. The order in which the .sts points
278    make up the boundary are given in order.txt file
279
280    FIXME:
281    Point coordinates are stored in relative eastings and northings.
282    But boundary is produced in absolute coordinates
283    """
284
[7778]285    if sts_filename.endswith('.sts'):
286        stsname_postfixed = sts_filename
287    else:
288        stsname_postfixed = sts_filename + '.sts'
289
[7760]290    try:
[7778]291        fid = NetCDFFile(stsname_postfixed, netcdf_mode_r)
292    except IOError:
293        msg = 'Cannot open %s' % stsname_postfixed
[7766]294        raise IOError(msg)
[7760]295
296    xllcorner = fid.xllcorner[0]
297    yllcorner = fid.yllcorner[0]
298
299    #Points stored in sts file are normalised to [xllcorner,yllcorner] but
300    #we cannot assume that boundary polygon will be. At least the
301    #additional points specified by the user after this function is called
302    x = fid.variables['x'][:] + xllcorner
303    y = fid.variables['y'][:] + yllcorner
304
[7762]305    x = num.reshape(x, (len(x), 1))
306    y = num.reshape(y, (len(y), 1))
[7760]307    sts_points = num.concatenate((x,y), axis=1)
308
309    return sts_points.tolist()
310
311
312
Note: See TracBrowser for help on using the repository browser.