source: branches/numpy_anuga_validation/automated_validation_tests/patong_beach_validation/cmpsww.py @ 6906

Last change on this file since 6906 was 6906, checked in by rwilson, 15 years ago

Patong beach validation changes plus back-merge changes.

  • Property svn:executable set to *
File size: 12.1 KB
Line 
1#!/usr/bin/env python
2
3'''
4A program to compare two SWW  files for "equality".
5
6This program makes lots of assumptions about the structure of the SWW files,
7so if that structure changes, this program must change.
8'''
9
10import sys
11import os
12import os.path
13import getopt
14from Scientific.IO.NetCDF import NetCDFFile
15import numpy as num
16from anuga.config import netcdf_mode_r
17
18
19#####
20# Various constants.
21#####
22
23# Global attributes that should exist and be same in both files
24# Don't have to have all of these, and we don't care about others.
25expect_global_attributes = ['smoothing', 'vertices_are_stored_uniquely',
26                            'order', 'starttime',
27                            'xllcorner', 'yllcorner',
28                            'zone', 'false_easting', 'false_northing',
29                            'datum', 'projection', 'units']
30
31# dimensions expected, with expected values (None means unknown)
32expected_dimensions = {'number_of_volumes': None,
33                       'number_of_vertices': 3,
34                       'numbers_in_range': 2,
35                       'number_of_points': None,
36                       'number_of_timesteps': None}
37
38# Variables expected, with expected dimensions.
39# Don't have to have all of these, and we don't care about others.
40expected_variables = {'x': ('number_of_points',),
41                      'y': ('number_of_points',),
42                      'elevation': ('number_of_points',),
43                      'elevation_range': ('numbers_in_range',),
44                      'z': ('number_of_points',),
45                      'volumes': ('number_of_volumes', 'number of vertices'),
46                      'time': ('number_of_timesteps',),
47                      'stage': ('numbers_in_range',),
48                      'stage_range': ('numbers_in_range',),
49                      'xmomentum': ('number_of_timesteps', 'number_of_points'),
50                      'xmomentum_range': ('numbers_in_range'),
51                      'ymomentum': ('number_of_timesteps', 'number_of_points'),
52                      'ymomentum_range': ('numbers_in_range')}
53
54##
55# @brief An exception to inform user of usage problems.
56class Usage(Exception):
57    def __init__(self, msg):
58        self.msg = msg
59
60
61##
62# @brief Compare two SWW files.
63# @param files A tuple of two filenames.
64# @param globals A list of global attribute names to compare.
65# @param timesteps A list of timesteps to compare at.
66# @param variables A list of variable names to compare.
67# @return Returns if files 'equal', else raises RuntimeError.
68def files_are_the_same(files, globals=None, timesteps=None, variables=None):
69    # split out the filenames and check they exist
70    (file1, file2) = files
71
72    error = False
73    error_msg = ''
74
75    try:
76        fid1 = NetCDFFile(file1, netcdf_mode_r)
77    except:
78        error_msg += "File '%s' can't be opened?\n" % file1
79        error = True
80
81    try:
82        fid2 = NetCDFFile(file2, netcdf_mode_r)
83    except:
84        error_msg += "File '%s' can't be opened?\n" % file2
85        error = True
86        fid1.close()
87
88    if error:
89        raise RuntimeError, error_msg
90
91    if globals is None:
92        globals = expect_global_attributes
93
94    #####
95    # First, check that files have the required structure
96    #####
97
98    # dimensions - only check expected dimensions
99    for key in expected_dimensions:
100        if key not in fid1.dimensions.keys():
101            error_msg += ("File %s doesn't contain dimension '%s'\n"
102                          % (file1, key))
103            error = True
104        if key not in fid2.dimensions.keys():
105            error_msg += ("File %s doesn't contain dimension '%s'\n"
106                          % (file2, key))
107            error = True
108
109    # now check that dimensions are the same length
110    # NOTE: DOESN'T CHECK 'UNLIMITED' DIMENSIONS YET! (get None at the moment)
111    for dim in expected_dimensions:
112        dim1_shape = fid1.dimensions.get(dim, None)
113        dim2_shape = fid2.dimensions.get(dim, None)
114        if dim1_shape and dim2_shape and dim1_shape != dim2_shape:
115            error_msg += ('File %s has %s dimension of size %s, '
116                          'file %s has that dimension of size %s\n'
117                          % (file1, dim, str(dim1_shape),
118                             file2, str(dim2_shape)))
119            error = True
120
121    # check that we have the required globals
122    if globals:
123        for glob in globals:
124            if glob not in dir(fid1):
125                error_msg += ("Global attribute '%s' isn't in file %s\n"
126                              % (glob, file1))
127                error = True
128            if glob not in dir(fid2):
129                error_msg += ("Global attribute '%s' isn't in file %s\n"
130                              % (glob, file2))
131                error = True
132    else:
133        # get list of global attributes
134        glob_vars1 = []
135        glob_vars2 = []
136        for glob in expect_global_attributes:
137            if glob in dir(fid1):
138                glob_vars1.append(glob)
139            if glob in dir(fid2):
140                glob_vars2.append(glob)
141
142        # now check attribute lists are same
143        if glob_vars1 != glob_vars2:
144            error_msg = ('Files differ in global attributes:\n'
145                         '%s: %s,\n'
146                         '%s: %s\n' % (file1, str(glob_vars1),
147                                     file2, str(glob_vars2)))
148            error = True
149
150    # get variables to test
151    if variables:
152        for var in variables:
153            if var not in fid1.variables.keys():
154                error_msg += ("Variable '%s' isn't in file %s\n"
155                              % (var, file1))
156                error = True
157            if var not in fid2.variables.keys():
158                error_msg += ("Variable '%s' isn't in file %s\n"
159                              % (var, file2))
160                error = True
161    else:
162        # check that variables are as expected in both files
163        var_names1 = []
164        var_names2 = []
165        for var_name in expected_variables:
166            if fid1.variables.has_key(var_name):
167                var_names1.append(var_name)
168            if fid2.variables.has_key(var_name):
169                var_names2.append(var_name)
170   
171        if var_names1 != var_names2:
172            error_msg += ('Variables are not the same between files: '
173                          '%s variables= %s, '
174                          '%s variables = %s\n'
175                          % (file1, str(var_names1), file2, str(var_names2)))
176            error = True
177        variables = var_names1
178
179    if error:
180        fid1.close()
181        fid2.close()
182        raise RuntimeError, error_msg
183
184    # get size of time dimension
185    num_timesteps1 = fid1.variables['time'].shape
186    num_timesteps2 = fid2.variables['time'].shape
187    if num_timesteps1 != num_timesteps2:
188        error_msg += ('Files have different number of timesteps: %s=%d, %s=%d\n'
189                      % (file1, num_timesteps1, file2, num_timesteps2))
190        error = True
191
192    if error:
193        fid1.close()
194        fid2.close()
195        raise RuntimeError, error_msg
196
197    num_timesteps = num_timesteps1[0]
198
199    # variable shapes same?
200    for var_name in variables:
201        var1 = fid1.variables[var_name]
202        var2 = fid2.variables[var_name]
203        var1_shape = var1.shape
204        var2_shape = var2.shape
205        if var1_shape != var2_shape:
206            error_msg += ('Files differ in variable %s shape:\n'
207                          '%s: %s,\n'
208                          '%s: %s\n'
209                          % (var_name, file1, str(var1_shape),
210                             file2, str(var2_shape)))
211            error = True
212            continue
213
214    if error:
215        fid1.close()
216        fid2.close()
217        raise RuntimeError, error_msg
218
219    #####
220    # Now check that actual data values are the same
221    #####
222
223    # check values of global attributes
224    for glob_name in globals:
225        g1 = getattr(fid1, glob_name)
226        g2 = getattr(fid2, glob_name)
227        if g1 != g2:
228            error_msg += ("Files differ in global '%s': "
229                          "%s: '%s', "
230                          "%s: '%s'\n"
231                          % (glob_name, file1, str(g1), file2, str(g2)))
232            error = True
233
234    # check data variables, be clever with time series data
235    for var_name in variables:
236        var_dims = expected_variables[var_name]
237        if (len(var_dims) > 1) and (var_dims[0] == 'number_of_timesteps'):
238            # time series, check by timestep block
239            for i in xrange(num_timesteps):
240                var1 = num.array(fid1.variables[var_name][i,:])
241                var2 = num.array(fid2.variables[var_name][i,:])
242                if not num.allclose(var1, var2):
243                    error_msg += ('Files differ in variable %s data:\n'
244                                  '%s: %s\n'
245                                  '%s: %s\n'
246                                  % (glob_name, file1, str(var1),
247                                                file2, str(var1)))
248                    error = True
249        else:
250            # simple data, check whole thing at once
251            var1 = num.array(fid1.variables[var_name][:])
252            var2 = num.array(fid2.variables[var_name][:])
253            if not num.allclose(var1, var2):
254                error_msg += ('Files differ in variable %s:\n'
255                              '%s: %s,\n'
256                              '%s: %s\n'
257                              % (var_name, file1, str(var1),
258                                           file2, str(var2)))
259                error = True
260
261    if error:
262        fid1.close()
263        fid2.close()
264        raise RuntimeError, error_msg
265
266    #####
267    # All OK, close files and signal EQUAL
268    #####
269
270    fid1.close()
271    fid2.close()
272
273    return
274
275
276##
277# @brief Return a usage string.
278def usage():
279    result = []
280    a = result.append
281    a('Usage: %s <options> <file1> <file2>\n' % ProgName)
282    a('where <options> is zero or more of:\n')
283    a('                   -h        print this help\n')
284    a("                   -a <val>  set absolute threshold of 'equivalent'\n")
285    a("                   -r <val>  set relative threshold of 'equivalent'\n")
286    a('                   -g <arg>  check only global attributes specified\n')
287    a('                             <arg> has the form <globname>[,<globname>[,...]]\n')
288    a('                   -t <arg>  check only timesteps specified\n')
289    a('                             <arg> has the form <starttime>[,<stoptime>[,<step>]]\n')
290    a('                   -v <arg>  check only the named variables\n')
291    a('                             <arg> has the form <varname>[,<varname>[,...]]\n')
292    a('and <file1> and <file2> are two SWW files to compare.\n')
293    a('\n')
294    a('The program exit status is one of:\n')
295    a('   0    the two files are equivalent\n')
296    a('   else the files are not equivalent.')
297    return ''.join(result)
298
299##
300# @brief Print a message to stderr.
301def warn(msg):
302    print >>sys.stderr, msg
303
304
305##
306# @brief
307# @param argv
308# @return The status code the program will exit with.
309def main(argv=None):
310    if argv is None:
311        argv = sys.argv
312
313    try:
314        try:
315            opts, args = getopt.getopt(argv[1:], 'hg:t:v:',
316                                       ['help', 'globals',
317                                        'variables', 'timesteps'])
318        except getopt.error, msg:
319            raise Usage(msg)
320    except Usage, err:
321        print >>sys.stderr, err.msg
322        print >>sys.stderr, "for help use --help"
323        return 2
324
325    # process options
326    globals = None
327    timesteps = None
328    variables = None
329    for opt, arg in opts:
330        if opt in ('-h', '--help'):
331            print usage()
332            sys.exit(0)
333        elif opt in ('-g', '--globals'):
334            globals = arg.split(',')
335        elif opt in ('-t', '--timesteps'):
336            timesteps = arg.split(',')
337        elif opt in ('-v', '--variables'):
338            variables = arg.split(',')
339
340    # process arguments
341    if len(args) != 2:
342        msg = usage()
343        print 'msg=%s' % msg
344        raise Usage(msg)
345
346    try:
347        files_are_the_same(args, globals=globals,
348                           timesteps=timesteps, variables=variables)
349    except RuntimeError, msg:
350         print msg
351         return 10
352
353
354if __name__ == "__main__":
355    global ProgName
356
357    ProgName = os.path.basename(sys.argv[0])
358
359    sys.exit(main())
360
Note: See TracBrowser for help on using the repository browser.