source: trunk/anuga_core/source/anuga/file_conversion/urs2sts.py @ 8466

Last change on this file since 8466 was 8149, checked in by wilsonr, 14 years ago

Removed '@brief' comments.

File size: 11.1 KB
Line 
1import numpy as num
2import log
3
4# ANUGA modules
5from anuga.config import netcdf_mode_r, netcdf_mode_w, netcdf_mode_a, \
6                            netcdf_float
7from anuga.utilities.numerical_tools import ensure_numeric
8from anuga.coordinate_transforms.geo_reference import Geo_reference, \
9     write_NetCDF_georeference, ensure_geo_reference
10
11# local modules
12from anuga.file.mux import read_mux2_py
13from anuga.file.mux import WAVEHEIGHT_MUX2_LABEL, EAST_VELOCITY_MUX2_LABEL, \
14                NORTH_VELOCITY_MUX2_LABEL
15from anuga.file.sts import Write_sts               
16               
17
18def urs2sts(basename_in, basename_out=None,
19            weights=None,
20            verbose=False,
21            origin=None,
22            zone=None,
23            central_meridian=None,           
24            mean_stage=0.0,
25            zscale=1.0,
26            ordering_filename=None):
27    """Convert URS mux2 format for wave propagation to sts format
28
29    Also convert latitude and longitude to UTM. All coordinates are
30    assumed to be given in the GDA94 datum
31
32    origin is a 3-tuple with geo referenced
33    UTM coordinates (zone, easting, northing)
34
35    inputs:
36
37    basename_in: list of source file prefixes
38
39        These are combined with the extensions:
40        WAVEHEIGHT_MUX2_LABEL = '-z-mux2' for stage
41        EAST_VELOCITY_MUX2_LABEL = '-e-mux2' xmomentum
42        NORTH_VELOCITY_MUX2_LABEL = '-n-mux2' and ymomentum
43
44        to create a 2D list of mux2 file. The rows are associated with each
45        quantity and must have the above extensions
46        the columns are the list of file prefixes.
47
48    ordering: a .txt file name specifying which mux2 gauge points are
49              to be stored. This is indicated by the index of the gauge
50              in the ordering file.
51
52              ordering file format:
53              1st line:    'index,longitude,latitude\n'
54              other lines: index,longitude,latitude
55
56              If ordering is None or ordering file is empty then
57               all points are taken in the order they
58              appear in the mux2 file.
59
60
61    output:
62      basename_out: name of sts file in which mux2 data is stored.
63     
64     
65     
66    NOTE: South is positive in mux files so sign of y-component of velocity is reverted
67    """
68
69    import os
70    from Scientific.IO.NetCDF import NetCDFFile
71    from operator import __and__
72
73    if not isinstance(basename_in, list):
74        if verbose: log.critical('Reading single source')
75        basename_in = [basename_in]
76
77    # This is the value used in the mux file format to indicate NAN data
78    # FIXME (Ole): This should be changed everywhere to IEEE NAN when
79    #              we upgrade to Numpy
80    NODATA = 99
81
82    # Check that basename is a list of strings
83    if not reduce(__and__, map(lambda z:isinstance(z,basestring), basename_in)):
84        msg= 'basename_in must be a string or list of strings'
85        raise Exception, msg
86
87    # Find the number of sources to be used
88    numSrc = len(basename_in)
89
90    # A weight must be specified for each source
91    if weights is None:
92        # Default is equal weighting
93        weights = num.ones(numSrc, num.float) / numSrc
94    else:
95        weights = ensure_numeric(weights)
96        msg = 'When combining multiple sources must specify a weight for ' \
97              'mux2 source file'
98        assert len(weights) == numSrc, msg
99
100    if verbose: log.critical('Weights used in urs2sts: %s' % str(weights))
101
102    # Check output filename
103    if basename_out is None:
104        msg = 'STS filename must be specified as basename_out ' \
105              'in function urs2sts'
106        raise Exception, msg
107
108    if basename_out.endswith('.sts'):
109        stsname = basename_out
110    else:
111        stsname = basename_out + '.sts'
112
113    # Create input filenames from basenames and check their existence
114    files_in = [[], [], []]
115    for files in basename_in:
116        files_in[0].append(files + WAVEHEIGHT_MUX2_LABEL),
117        files_in[1].append(files + EAST_VELOCITY_MUX2_LABEL)
118        files_in[2].append(files + NORTH_VELOCITY_MUX2_LABEL)
119
120    quantities = ['HA','UA','VA'] # Quantity names used in the MUX2 format
121    for i in range(len(quantities)):
122        for file_in in files_in[i]:
123            if (os.access(file_in, os.R_OK) == 0):
124                msg = 'File %s does not exist or is not accessible' % file_in
125                raise IOError, msg
126
127    # Establish permutation array
128    if ordering_filename is not None:
129        if verbose is True: log.critical('Reading ordering file %s'
130                                         % ordering_filename)
131
132        # Read ordering file
133        try:
134            fid = open(ordering_filename, 'r')
135            file_header = fid.readline().split(',')
136            ordering_lines = fid.readlines()
137            fid.close()
138        except:
139            msg = 'Cannot open %s' % ordering_filename
140            raise Exception, msg
141
142        reference_header = 'index, longitude, latitude\n'
143        reference_header_split = reference_header.split(',')
144        for i in range(3):
145            if not file_header[i].strip() == reference_header_split[i].strip():
146                msg = 'File must contain header: ' + reference_header
147                raise Exception, msg
148
149        if len(ordering_lines) < 2:
150            msg = 'File must contain at least two points'
151            raise Exception, msg
152
153        permutation = [int(line.split(',')[0]) for line in ordering_lines]
154        permutation = ensure_numeric(permutation)
155    else:
156        permutation = None
157
158    # Read MUX2 files
159    if (verbose): log.critical('reading mux2 file')
160
161    mux={}
162    for i, quantity in enumerate(quantities):
163        # For each quantity read the associated list of source mux2 file with
164        # extention associated with that quantity
165
166        times, latitudes, longitudes, elevation, mux[quantity], starttime \
167            = read_mux2_py(files_in[i], weights, permutation, verbose=verbose)
168
169        # Check that all quantities have consistent time and space information
170        if quantity != quantities[0]:
171            msg = '%s, %s and %s have inconsistent gauge data' \
172                  % (files_in[0], files_in[1], files_in[2])
173            assert num.allclose(times, times_old), msg
174            assert num.allclose(latitudes, latitudes_old), msg
175            assert num.allclose(longitudes, longitudes_old), msg
176            assert num.allclose(elevation, elevation_old), msg
177            assert num.allclose(starttime, starttime_old), msg
178        times_old = times
179        latitudes_old = latitudes
180        longitudes_old = longitudes
181        elevation_old = elevation
182        starttime_old = starttime
183
184        # Self check - can be removed to improve speed
185        #ref_longitudes = [float(line.split(',')[1]) for line in ordering_lines]
186        #ref_latitudes = [float(line.split(',')[2]) for line in ordering_lines]
187        #
188        #msg = 'Longitudes specified in ordering file do not match those ' \
189        #      'found in mux files. ' \
190        #      'I got %s instead of %s (only beginning shown)' \
191        #      % (str(longitudes[:10]) + '...',
192        #         str(ref_longitudes[:10]) + '...')
193        #assert allclose(longitudes, ref_longitudes), msg
194        #
195        #msg = 'Latitudes specified in ordering file do not match those ' \
196        #      'found in mux files. '
197        #      'I got %s instead of %s (only beginning shown)' \
198        #      % (str(latitudes[:10]) + '...',
199        #         str(ref_latitudes[:10]) + '...')
200        #assert allclose(latitudes, ref_latitudes), msg
201
202    # Store timeseries in STS file
203    msg = 'File is empty and or clipped region not in file region'
204    assert len(latitudes > 0), msg
205
206    number_of_points = latitudes.shape[0]      # Number of stations retrieved
207    number_of_times = times.shape[0]           # Number of timesteps
208    number_of_latitudes = latitudes.shape[0]   # Number latitudes
209    number_of_longitudes = longitudes.shape[0] # Number longitudes
210
211    # The permutation vector of contains original indices
212    # as given in ordering file or None in which case points
213    # are assigned the trivial indices enumerating them from
214    # 0 to number_of_points-1
215    if permutation is None:
216        permutation = num.arange(number_of_points, dtype=num.int)
217
218    # NetCDF file definition
219    outfile = NetCDFFile(stsname, netcdf_mode_w)
220
221    description = 'Converted from URS mux2 files: %s' % basename_in
222
223    # Create new file
224    sts = Write_sts()
225    sts.store_header(outfile,
226                     times+starttime,
227                     number_of_points,
228                     description=description,
229                     verbose=verbose,
230                     sts_precision=netcdf_float)
231
232    # Store
233    from anuga.coordinate_transforms.redfearn import redfearn
234
235    x = num.zeros(number_of_points, num.float)  # Easting
236    y = num.zeros(number_of_points, num.float)  # Northing
237
238    # Check zone boundaries
239    if zone is None:
240        refzone, _, _ = redfearn(latitudes[0], longitudes[0],
241                                 central_meridian=central_meridian)
242    else:
243        refzone = zone
244
245    old_zone = refzone
246
247    for i in range(number_of_points):
248        computed_zone, easting, northing = redfearn(latitudes[i], longitudes[i],
249                                                    zone=zone,
250                                                    central_meridian=central_meridian)
251        x[i] = easting
252        y[i] = northing
253        if computed_zone != refzone:
254            msg = 'All sts gauges need to be in the same zone. \n'
255            msg += 'offending gauge:Zone %d,%.4f, %4f\n' \
256                   % (computed_zone, easting, northing)
257            msg += 'previous gauge:Zone %d,%.4f, %4f' \
258                   % (old_zone, old_easting, old_northing)
259            raise Exception, msg
260        old_zone = computed_zone
261        old_easting = easting
262        old_northing = northing
263
264    if origin is None:
265        origin = Geo_reference(refzone, min(x), min(y))
266    geo_ref = write_NetCDF_georeference(origin, outfile)
267
268    elevation = num.resize(elevation, outfile.variables['elevation'][:].shape)
269    outfile.variables['permutation'][:] = permutation.astype(num.int32) # Opteron 64
270    outfile.variables['x'][:] = x - geo_ref.get_xllcorner()
271    outfile.variables['y'][:] = y - geo_ref.get_yllcorner()
272    outfile.variables['elevation'][:] = elevation
273
274    stage = outfile.variables['stage']
275    xmomentum = outfile.variables['xmomentum']
276    ymomentum = outfile.variables['ymomentum']
277
278    if verbose: log.critical('Converting quantities')
279
280    for j in range(len(times)):
281        for i in range(number_of_points):
282            ha = mux['HA'][i,j]
283            ua = mux['UA'][i,j]
284            va = mux['VA'][i,j]
285            if ha == NODATA:
286                if verbose:
287                    msg = 'Setting nodata value %d to 0 at time = %f, ' \
288                          'point = %d' % (ha, times[j], i)
289                    log.critical(msg)
290                ha = 0.0
291                ua = 0.0
292                va = 0.0
293
294            w = zscale*ha + mean_stage
295            h = w - elevation[i]
296            stage[j,i] = w
297
298            xmomentum[j,i] = ua * h
299            ymomentum[j,i] = -va * h # South is positive in mux files
300
301
302    outfile.close()
303   
304    if verbose:
305        log.critical('Wrote sts file ' + stsname)   
306   
307
Note: See TracBrowser for help on using the repository browser.