source: anuga_core/source/anuga/abstract_2d_finite_volumes/util.py @ 6175

Last change on this file since 6175 was 6175, checked in by ole, 15 years ago

Start time adjustment and better diagnostics for time_limit

File size: 99.7 KB
Line 
1"""This module contains various auxiliary function used by pyvolution.
2
3It is also a clearing house for functions that may later earn a module
4of their own.
5"""
6
7import anuga.utilities.polygon
8import sys
9import os
10
11from os import remove, mkdir, access, F_OK, R_OK, W_OK, sep,getcwd
12from os.path import exists, basename, split,join
13from warnings import warn
14from shutil import copy
15
16from anuga.utilities.numerical_tools import ensure_numeric
17from Scientific.IO.NetCDF import NetCDFFile
18   
19from anuga.geospatial_data.geospatial_data import ensure_absolute
20from math import sqrt, atan, degrees
21
22# FIXME (Ole): Temporary short cuts -
23# FIXME (Ole): remove and update scripts where they are used
24from anuga.utilities.system_tools import get_revision_number
25from anuga.utilities.system_tools import store_version_info
26
27from anuga.config import netcdf_mode_r, netcdf_mode_w, netcdf_mode_a
28
29import Numeric as num
30
31
32##
33# @brief Read time history of data from NetCDF file, return callable object.
34# @param filename  Name of .sww or .tms file.
35# @param domain Associated domain object.
36# @param quantities Name of quantity to be interpolated or a list of names.
37# @param interpolation_points List of absolute UTM coordinates for points
38#                             (N x 2) or geospatial object or
39#                             points file name at which values are sought.
40# @param time_thinning
41# @param verbose True if this function is to be verbose.
42# @param use_cache True means that caching of intermediate result is attempted.
43# @param boundary_polygon
44# @return A callable object.
45def file_function(filename,
46                  domain=None,
47                  quantities=None,
48                  interpolation_points=None,
49                  time_thinning=1,
50                  time_limit=None,
51                  verbose=False,
52                  use_cache=False,
53                  boundary_polygon=None):
54    """Read time history of spatial data from NetCDF file and return
55    a callable object.
56
57    Input variables:
58   
59    filename - Name of sww or tms file
60       
61       If the file has extension 'sww' then it is assumed to be spatio-temporal
62       or temporal and the callable object will have the form f(t,x,y) or f(t)
63       depending on whether the file contains spatial data
64
65       If the file has extension 'tms' then it is assumed to be temporal only
66       and the callable object will have the form f(t)
67
68       Either form will return interpolated values based on the input file
69       using the underlying interpolation_function.
70
71    domain - Associated domain object   
72       If domain is specified, model time (domain.starttime)
73       will be checked and possibly modified.
74   
75       All times are assumed to be in UTC
76       
77       All spatial information is assumed to be in absolute UTM coordinates.
78
79    quantities - the name of the quantity to be interpolated or a
80                 list of quantity names. The resulting function will return
81                 a tuple of values - one for each quantity
82                 If quantities are None, the default quantities are
83                 ['stage', 'xmomentum', 'ymomentum']
84                 
85
86    interpolation_points - list of absolute UTM coordinates for points (N x 2)
87    or geospatial object or points file name at which values are sought
88
89    time_thinning -
90
91    verbose -
92
93    use_cache: True means that caching of intermediate result of
94               Interpolation_function is attempted
95
96    boundary_polygon -
97
98   
99    See Interpolation function in anuga.fit_interpolate.interpolation for
100    further documentation
101    """
102
103    # FIXME (OLE): Should check origin of domain against that of file
104    # In fact, this is where origin should be converted to that of domain
105    # Also, check that file covers domain fully.
106
107    # Take into account:
108    # - domain's georef
109    # - sww file's georef
110    # - interpolation points as absolute UTM coordinates
111
112    if quantities is None:
113        if verbose:
114            msg = 'Quantities specified in file_function are None,'
115            msg += ' so I will use stage, xmomentum, and ymomentum in that order'
116            print msg
117        quantities = ['stage', 'xmomentum', 'ymomentum']
118
119    # Use domain's startime if available
120    if domain is not None:   
121        domain_starttime = domain.get_starttime()
122    else:
123        domain_starttime = None
124
125    # Build arguments and keyword arguments for use with caching or apply.
126    args = (filename,)
127
128    # FIXME (Ole): Caching this function will not work well
129    # if domain is passed in as instances change hash code.
130    # Instead we pass in those attributes that are needed (and return them
131    # if modified)
132    kwargs = {'quantities': quantities,
133              'interpolation_points': interpolation_points,
134              'domain_starttime': domain_starttime,
135              'time_thinning': time_thinning,     
136              'time_limit': time_limit,                                 
137              'verbose': verbose,
138              'boundary_polygon': boundary_polygon}
139
140    # Call underlying engine with or without caching
141    if use_cache is True:
142        try:
143            from caching import cache
144        except:
145            msg = 'Caching was requested, but caching module'+\
146                  'could not be imported'
147            raise msg
148
149        f, starttime = cache(_file_function,
150                             args, kwargs,
151                             dependencies=[filename],
152                             compression=False,                 
153                             verbose=verbose)
154    else:
155        f, starttime = apply(_file_function,
156                             args, kwargs)
157
158    #FIXME (Ole): Pass cache arguments, such as compression, in some sort of
159    #structure
160
161    f.starttime = starttime
162    f.filename = filename
163   
164    if domain is not None:
165        #Update domain.startime if it is *earlier* than starttime from file
166        if starttime > domain.starttime:
167            msg = 'WARNING: Start time as specified in domain (%f)'\
168                  %domain.starttime
169            msg += ' is earlier than the starttime of file %s (%f).'\
170                     %(filename, starttime)
171            msg += ' Modifying domain starttime accordingly.'
172           
173            if verbose: print msg
174
175            domain.set_starttime(starttime) #Modifying model time
176
177            if verbose: print 'Domain starttime is now set to %f'\
178               %domain.starttime
179    return f
180
181
182##
183# @brief ??
184# @param filename  Name of .sww or .tms file.
185# @param domain Associated domain object.
186# @param quantities Name of quantity to be interpolated or a list of names.
187# @param interpolation_points List of absolute UTM coordinates for points
188#                             (N x 2) or geospatial object or
189#                             points file name at which values are sought.
190# @param time_thinning
191# @param verbose True if this function is to be verbose.
192# @param use_cache True means that caching of intermediate result is attempted.
193# @param boundary_polygon
194def _file_function(filename,
195                   quantities=None,
196                   interpolation_points=None,
197                   domain_starttime=None,
198                   time_thinning=1,
199                   time_limit=None,
200                   verbose=False,
201                   boundary_polygon=None):
202    """Internal function
203   
204    See file_function for documentatiton
205    """
206
207    assert type(filename) == type(''),\
208               'First argument to File_function must be a string'
209
210    try:
211        fid = open(filename)
212    except Exception, e:
213        msg = 'File "%s" could not be opened: Error="%s"' % (filename, e)
214        raise msg
215
216    # read first line of file, guess file type
217    line = fid.readline()
218    fid.close()
219
220    if line[:3] == 'CDF':
221        return get_netcdf_file_function(filename,
222                                        quantities,
223                                        interpolation_points,
224                                        domain_starttime,
225                                        time_thinning=time_thinning,
226                                        time_limit=time_limit,
227                                        verbose=verbose,
228                                        boundary_polygon=boundary_polygon)
229    else:
230        # FIXME (Ole): Could add csv file here to address Ted Rigby's
231        # suggestion about reading hydrographs.
232        # This may also deal with the gist of ticket:289
233        raise 'Must be a NetCDF File'
234
235
236##
237# @brief ??
238# @param filename  Name of .sww or .tms file.
239# @param quantity_names Name of quantity to be interpolated or a list of names.
240# @param interpolation_points List of absolute UTM coordinates for points
241#                             (N x 2) or geospatial object or
242#                             points file name at which values are sought.
243# @param domain_starttime Start time from domain object.
244# @param time_thinning ??
245# @param verbose True if this function is to be verbose.
246# @param boundary_polygon ??
247# @return A callable object.
248def get_netcdf_file_function(filename,
249                             quantity_names=None,
250                             interpolation_points=None,
251                             domain_starttime=None,                           
252                             time_thinning=1,                 
253                             time_limit=None,           
254                             verbose=False,
255                             boundary_polygon=None):
256    """Read time history of spatial data from NetCDF sww file and
257    return a callable object f(t,x,y)
258    which will return interpolated values based on the input file.
259
260    Model time (domain_starttime)
261    will be checked, possibly modified and returned
262   
263    All times are assumed to be in UTC
264
265    See Interpolation function for further documetation
266    """
267
268    # FIXME: Check that model origin is the same as file's origin
269    # (both in UTM coordinates)
270    # If not - modify those from file to match domain
271    # (origin should be passed in)
272    # Take this code from e.g. dem2pts in data_manager.py
273    # FIXME: Use geo_reference to read and write xllcorner...
274
275    import time, calendar, types
276    from anuga.config import time_format
277
278    # Open NetCDF file
279    if verbose: print 'Reading', filename
280
281    fid = NetCDFFile(filename, netcdf_mode_r)
282
283    if type(quantity_names) == types.StringType:
284        quantity_names = [quantity_names]       
285
286    if quantity_names is None or len(quantity_names) < 1:
287        msg = 'No quantities are specified in file_function'
288        raise Exception, msg
289 
290    if interpolation_points is not None:
291        interpolation_points = ensure_absolute(interpolation_points)
292        msg = 'Points must by N x 2. I got %d' % interpolation_points.shape[1]
293        assert interpolation_points.shape[1] == 2, msg
294
295    # Now assert that requested quantitites (and the independent ones)
296    # are present in file
297    missing = []
298    for quantity in ['time'] + quantity_names:
299        if not fid.variables.has_key(quantity):
300            missing.append(quantity)
301
302    if len(missing) > 0:
303        msg = 'Quantities %s could not be found in file %s'\
304              % (str(missing), filename)
305        fid.close()
306        raise Exception, msg
307
308    # Decide whether this data has a spatial dimension
309    spatial = True
310    for quantity in ['x', 'y']:
311        if not fid.variables.has_key(quantity):
312            spatial = False
313
314    if filename[-3:] == 'tms' and spatial is True:
315        msg = 'Files of type tms must not contain spatial  information'
316        raise msg
317
318    if filename[-3:] == 'sww' and spatial is False:
319        msg = 'Files of type sww must contain spatial information'       
320        raise msg
321
322    if filename[-3:] == 'sts' and spatial is False:
323        #What if mux file only contains one point
324        msg = 'Files of type sts must contain spatial information'       
325        raise msg
326
327    if filename[-3:] == 'sts' and boundary_polygon is None:
328        #What if mux file only contains one point
329        msg = 'Files of type sts require boundary polygon'       
330        raise msg
331
332    # Get first timestep
333    try:
334        starttime = fid.starttime[0]
335    except ValueError:
336        msg = 'Could not read starttime from file %s' % filename
337        raise msg
338
339    # Get variables
340    # if verbose: print 'Get variables'   
341    time = fid.variables['time'][:]   
342    # FIXME(Ole): Is time monotoneous?
343
344    # Apply time limit if requested
345    upper_time_index = len(time)   
346    msg = 'Time vector obtained from file %s has length 0' % filename
347    assert upper_time_index > 0, msg
348   
349    if time_limit is not None:
350        # Adjust given time limit to given start time
351        time_limit = time_limit - starttime
352
353        # Find limit point
354        for i, t in enumerate(time):
355            if t > time_limit:
356                upper_time_index = i
357                break
358               
359        msg = 'Time vector is zero. Requested time limit is %f' % time_limit
360        assert upper_time_index > 0, msg
361
362        if time_limit < time[-1] and verbose is True:
363            print 'Limited time vector from %.2fs to %.2fs'\
364                  % (time[-1], time_limit)
365
366    time = time[:upper_time_index]
367
368
369   
370   
371    # Get time independent stuff
372    if spatial:
373        # Get origin
374        xllcorner = fid.xllcorner[0]
375        yllcorner = fid.yllcorner[0]
376        zone = fid.zone[0]       
377
378        x = fid.variables['x'][:]
379        y = fid.variables['y'][:]
380        if filename.endswith('sww'):
381            triangles = fid.variables['volumes'][:]
382
383        x = num.reshape(x, (len(x),1))
384        y = num.reshape(y, (len(y),1))
385        vertex_coordinates = num.concatenate((x,y), axis=1) #m x 2 array
386
387        if boundary_polygon is not None:
388            #removes sts points that do not lie on boundary
389            boundary_polygon=ensure_numeric(boundary_polygon)
390            boundary_polygon[:,0] -= xllcorner
391            boundary_polygon[:,1] -= yllcorner
392            temp=[]
393            boundary_id=[]
394            gauge_id=[]
395            for i in range(len(boundary_polygon)):
396                for j in range(len(x)):
397                    if num.allclose(vertex_coordinates[j],boundary_polygon[i],1e-4):
398                        #FIXME:
399                        #currently gauges lat and long is stored as float and
400                        #then cast to double. This cuases slight repositioning
401                        #of vertex_coordinates.
402                        temp.append(boundary_polygon[i])
403                        gauge_id.append(j)
404                        boundary_id.append(i)
405                        break
406            gauge_neighbour_id=[]
407            for i in range(len(boundary_id)-1):
408                if boundary_id[i]+1==boundary_id[i+1]:
409                    gauge_neighbour_id.append(i+1)
410                else:
411                    gauge_neighbour_id.append(-1)
412            if boundary_id[len(boundary_id)-1]==len(boundary_polygon)-1 \
413               and boundary_id[0]==0:
414                gauge_neighbour_id.append(0)
415            else:
416                gauge_neighbour_id.append(-1)
417            gauge_neighbour_id=ensure_numeric(gauge_neighbour_id)
418
419            if len(num.compress(gauge_neighbour_id>=0,gauge_neighbour_id)) \
420               != len(temp)-1:
421                msg='incorrect number of segments'
422                raise msg
423            vertex_coordinates=ensure_numeric(temp)
424            if len(vertex_coordinates)==0:
425                msg = 'None of the sts gauges fall on the boundary'
426                raise msg
427        else:
428            gauge_neighbour_id=None
429
430        if interpolation_points is not None:
431            # Adjust for georef
432            interpolation_points[:,0] -= xllcorner
433            interpolation_points[:,1] -= yllcorner       
434    else:
435        gauge_neighbour_id=None
436       
437    if domain_starttime is not None:
438        # If domain_startime is *later* than starttime,
439        # move time back - relative to domain's time
440        if domain_starttime > starttime:
441            time = time - domain_starttime + starttime
442
443        # FIXME Use method in geo to reconcile
444        # if spatial:
445        # assert domain.geo_reference.xllcorner == xllcorner
446        # assert domain.geo_reference.yllcorner == yllcorner
447        # assert domain.geo_reference.zone == zone       
448       
449    if verbose:
450        print 'File_function data obtained from: %s' %filename
451        print '  References:'
452        #print '    Datum ....' #FIXME
453        if spatial:
454            print '    Lower left corner: [%f, %f]'\
455                  %(xllcorner, yllcorner)
456        print '    Start time:   %f' %starttime               
457       
458   
459    # Produce values for desired data points at
460    # each timestep for each quantity
461    quantities = {}
462    for i, name in enumerate(quantity_names):
463        quantities[name] = fid.variables[name][:]
464        if boundary_polygon is not None:
465            #removes sts points that do not lie on boundary
466            quantities[name] = num.take(quantities[name],gauge_id,1)
467           
468    # Close sww, tms or sts netcdf file         
469    fid.close()
470
471    from anuga.fit_interpolate.interpolate import Interpolation_function
472
473    if not spatial:
474        vertex_coordinates = triangles = interpolation_points = None
475    if filename[-3:] == 'sts':#added
476        triangles = None
477        #vertex coordinates is position of urs gauges
478
479    # Return Interpolation_function instance as well as
480    # starttime for use to possible modify that of domain
481    return (Interpolation_function(time,
482                                   quantities,
483                                   quantity_names,
484                                   vertex_coordinates,
485                                   triangles,
486                                   interpolation_points,
487                                   time_thinning=time_thinning,
488                                   verbose=verbose,
489                                   gauge_neighbour_id=gauge_neighbour_id),
490            starttime)
491
492    # NOTE (Ole): Caching Interpolation function is too slow as
493    # the very long parameters need to be hashed.
494
495
496##
497# @brief Replace multiple substrings in a string.
498# @param text The string to operate on.
499# @param dictionary A dict containing replacements, key->value.
500# @return The new string.
501def multiple_replace(text, dictionary):
502    """Multiple replace of words in text
503
504    text:       String to be modified
505    dictionary: Mapping of words that are to be substituted
506
507    Python Cookbook 3.14 page 88 and page 90
508    http://code.activestate.com/recipes/81330/
509    """
510
511    import re
512   
513    #Create a regular expression from all of the dictionary keys
514    #matching only entire words
515    regex = re.compile(r'\b'+ \
516                       r'\b|\b'.join(map(re.escape, dictionary.keys()))+ \
517                       r'\b' )
518
519    #For each match, lookup the corresponding value in the dictionary
520    return regex.sub(lambda match: dictionary[match.group(0)], text)
521
522
523##
524# @brief Apply arbitrary expressions to the values of a dict.
525# @param expression A string expression to apply.
526# @param dictionary The dictionary to apply the expression to.
527def apply_expression_to_dictionary(expression, dictionary):
528    """Apply arbitrary expression to values of dictionary
529
530    Given an expression in terms of the keys, replace key by the
531    corresponding values and evaluate.
532
533    expression: Arbitrary, e.g. arithmetric, expression relating keys
534                from dictionary.
535
536    dictionary: Mapping between symbols in expression and objects that
537                will be evaluated by expression.
538                Values in dictionary must support operators given in
539                expression e.g. by overloading
540
541    Due to a limitation with Numeric, this can not evaluate 0/0
542    In general, the user can fix by adding 1e-30 to the numerator.
543    SciPy core can handle this situation.
544    """
545
546    import types
547    import re
548
549    assert isinstance(expression, basestring)
550    assert type(dictionary) == types.DictType
551
552    #Convert dictionary values to textual representations suitable for eval
553    D = {}
554    for key in dictionary:
555        D[key] = 'dictionary["%s"]' % key
556
557    #Perform substitution of variables   
558    expression = multiple_replace(expression, D)
559
560    #Evaluate and return
561    try:
562        return eval(expression)
563    except NameError, e:
564        msg = 'Expression "%s" could not be evaluated: %s' % (expression, e)
565        raise NameError, msg
566    except ValueError, e:
567        msg = 'Expression "%s" could not be evaluated: %s' % (expression, e)
568        raise ValueError, msg
569
570
571##
572# @brief Format a float into a string.
573# @param value Float value to format.
574# @param format The format to use (%.2f is default).
575# @return The formatted float as a string.
576def get_textual_float(value, format = '%.2f'):
577    """Get textual representation of floating point numbers
578    and accept None as valid entry
579
580    format is a string - default = '%.2f'
581    """
582
583    if value is None:
584        return 'None'
585    else:
586        try:
587            float(value)
588        except:
589            # May this is a vector
590            if len(value) > 1:
591                s = '('
592                for v in value:
593                    s += get_textual_float(v, format) + ', '
594                   
595                s = s[:-2] + ')' # Strip trailing comma and close
596                return s
597            else:
598                raise 'Illegal input to get_textual_float:', value
599        else:
600            return format % float(value)
601
602
603#################################################################################
604# OBSOLETE STUFF
605#################################################################################
606
607# @note TEMP
608def angle(v1, v2):
609    """Temporary Interface to new location"""
610
611    import anuga.utilities.numerical_tools as NT       
612   
613    msg = 'angle has moved from util.py.  '
614    msg += 'Please use "from anuga.utilities.numerical_tools import angle"'
615    warn(msg, DeprecationWarning) 
616
617    return NT.angle(v1, v2)
618
619   
620# @note TEMP
621def anglediff(v0, v1):
622    """Temporary Interface to new location"""
623
624    import anuga.utilities.numerical_tools as NT
625   
626    msg = 'anglediff has moved from util.py.  '
627    msg += 'Please use "from anuga.utilities.numerical_tools import anglediff"'
628    warn(msg, DeprecationWarning) 
629
630    return NT.anglediff(v0, v1)   
631
632   
633# @note TEMP
634def mean(x):
635    """Temporary Interface to new location"""
636
637    import anuga.utilities.numerical_tools as NT   
638   
639    msg = 'mean has moved from util.py.  '
640    msg += 'Please use "from anuga.utilities.numerical_tools import mean"'
641    warn(msg, DeprecationWarning) 
642
643    return NT.mean(x)   
644
645
646# @note TEMP
647def point_on_line(*args, **kwargs):
648    """Temporary Interface to new location"""
649
650    msg = 'point_on_line has moved from util.py.  '
651    msg += 'Please use "from anuga.utilities.polygon import point_on_line"'
652    warn(msg, DeprecationWarning) 
653
654    return utilities.polygon.point_on_line(*args, **kwargs)     
655
656   
657# @note TEMP
658def inside_polygon(*args, **kwargs):
659    """Temporary Interface to new location"""
660
661    print 'inside_polygon has moved from util.py.  ',
662    print 'Please use "from anuga.utilities.polygon import inside_polygon"'
663
664    return utilities.polygon.inside_polygon(*args, **kwargs)   
665
666   
667# @note TEMP
668def outside_polygon(*args, **kwargs):
669    """Temporary Interface to new location"""
670
671    print 'outside_polygon has moved from util.py.  ',
672    print 'Please use "from anuga.utilities.polygon import outside_polygon"'
673
674    return utilities.polygon.outside_polygon(*args, **kwargs)   
675
676
677# @note TEMP
678def separate_points_by_polygon(*args, **kwargs):
679    """Temporary Interface to new location"""
680
681    print 'separate_points_by_polygon has moved from util.py.  ',
682    print 'Please use "from anuga.utilities.polygon import ' \
683          'separate_points_by_polygon"'
684
685    return utilities.polygon.separate_points_by_polygon(*args, **kwargs)   
686
687
688# @note TEMP
689def read_polygon(*args, **kwargs):
690    """Temporary Interface to new location"""
691
692    print 'read_polygon has moved from util.py.  ',
693    print 'Please use "from anuga.utilities.polygon import read_polygon"'
694
695    return utilities.polygon.read_polygon(*args, **kwargs)   
696
697
698# @note TEMP
699def populate_polygon(*args, **kwargs):
700    """Temporary Interface to new location"""
701
702    print 'populate_polygon has moved from util.py.  ',
703    print 'Please use "from anuga.utilities.polygon import populate_polygon"'
704
705    return utilities.polygon.populate_polygon(*args, **kwargs)   
706
707
708#################################################################################
709# End of obsolete stuff ?
710#################################################################################
711
712# @note TEMP
713def start_screen_catcher(dir_name, myid='', numprocs='', extra_info='',
714                         verbose=False):
715    """Temporary Interface to new location"""
716    from anuga.shallow_water.data_manager import start_screen_catcher \
717         as dm_start_screen_catcher
718
719    print 'start_screen_catcher has moved from util.py.  ',
720    print 'Please use "from anuga.shallow_water.data_manager import ' \
721          'start_screen_catcher"'
722   
723    return dm_start_screen_catcher(dir_name, myid='', numprocs='',
724                                   extra_info='', verbose=False)
725
726
727##
728# @brief Read a .sww file and plot the time series.
729# @param swwfiles Dictionary of .sww files.
730# @param gauge_filename Name of gauge file.
731# @param production_dirs ??
732# @param report If True, write figures to report directory.
733# @param reportname Name of generated report (if any).
734# @param plot_quantity List containing quantities to plot.
735# @param generate_fig If True, generate figures as well as CSV files.
736# @param surface If True, then generate solution surface with 3d plot.
737# @param time_min Beginning of user defined time range for plotting purposes.
738# @param time_max End of user defined time range for plotting purposes.
739# @param time_thinning ??
740# @param time_unit ??
741# @param title_on If True, export standard graphics with title.
742# @param use_cache If True, use caching.
743# @param verbose If True, this function is verbose.
744def sww2timeseries(swwfiles,
745                   gauge_filename,
746                   production_dirs,
747                   report=None,
748                   reportname=None,
749                   plot_quantity=None,
750                   generate_fig=False,
751                   surface=None,
752                   time_min=None,
753                   time_max=None,
754                   time_thinning=1,                   
755                   time_unit=None,
756                   title_on=None,
757                   use_cache=False,
758                   verbose=False):
759    """ Read sww file and plot the time series for the
760    prescribed quantities at defined gauge locations and
761    prescribed time range.
762
763    Input variables:
764
765    swwfiles        - dictionary of sww files with label_ids (used in
766                      generating latex output. It will be part of
767                      the directory name of file_loc (typically the timestamp).
768                      Helps to differentiate latex files for different
769                      simulations for a particular scenario. 
770                    - assume that all conserved quantities have been stored
771                    - assume each sww file has been simulated with same timestep
772   
773    gauge_filename  - name of file containing gauge data
774                        - easting, northing, name , elevation?
775                    - OR (this is not yet done)
776                        - structure which can be converted to a Numeric array,
777                          such as a geospatial data object
778                     
779    production_dirs -  A list of list, example {20061101_121212: '1 in 10000',
780                                                'boundaries': 'urs boundary'}
781                      this will use the second part as the label and the
782                      first part as the ?
783                      #FIXME: Is it a list or a dictionary
784                      # This is probably obsolete by now
785                     
786    report          - if True, then write figures to report_figures directory in
787                      relevant production directory
788                    - if False, figures are already stored with sww file
789                    - default to False
790
791    reportname      - name for report if wishing to generate report
792   
793    plot_quantity   - list containing quantities to plot, they must
794                      be the name of an existing quantity or one of
795                      the following possibilities
796                    - possibilities:
797                        - stage; 'stage'
798                        - depth; 'depth'
799                        - speed; calculated as absolute momentum
800                         (pointwise) divided by depth; 'speed'
801                        - bearing; calculated as the angle of the momentum
802                          vector (xmomentum, ymomentum) from the North; 'bearing'
803                        - absolute momentum; calculated as
804                          sqrt(xmomentum^2 + ymomentum^2); 'momentum'
805                        - x momentum; 'xmomentum'
806                        - y momentum; 'ymomentum'
807                    - default will be ['stage', 'speed', 'bearing']
808
809    generate_fig     - if True, generate figures as well as csv file
810                     - if False, csv files created only
811                     
812    surface          - if True, then generate solution surface with 3d plot
813                       and save to current working directory
814                     - default = False
815   
816    time_min         - beginning of user defined time range for plotting purposes
817                        - default will be first available time found in swwfile
818                       
819    time_max         - end of user defined time range for plotting purposes
820                        - default will be last available time found in swwfile
821                       
822    title_on        - if True, export standard graphics with title
823                    - if False, export standard graphics without title
824
825
826    Output:
827   
828    - time series data stored in .csv for later use if required.
829      Name = gauges_timeseries followed by gauge name
830    - latex file will be generated in same directory as where script is
831      run (usually production scenario directory.
832      Name = latexoutputlabel_id.tex
833
834    Other important information:
835   
836    It is assumed that the used has stored all the conserved quantities
837    and elevation during the scenario run, i.e.
838    ['stage', 'elevation', 'xmomentum', 'ymomentum']
839    If this has not occurred then sww2timeseries will not work.
840
841
842    Usage example
843    texname = sww2timeseries({project.boundary_name + '.sww': ''},
844                             project.polygons_dir + sep + 'boundary_extent.csv',
845                             project.anuga_dir,
846                             report = False,
847                             plot_quantity = ['stage', 'speed', 'bearing'],
848                             time_min = None,
849                             time_max = None,
850                             title_on = True,   
851                             verbose = True)
852   
853    """
854
855    msg = 'NOTE: A new function is available to create csv files from sww '
856    msg += 'files called sww2csv_gauges in anuga.abstract_2d_finite_volumes.util'
857    msg += ' PLUS another new function to create graphs from csv files called '
858    msg += 'csv2timeseries_graphs in anuga.abstract_2d_finite_volumes.util'
859    print msg
860   
861    k = _sww2timeseries(swwfiles,
862                        gauge_filename,
863                        production_dirs,
864                        report,
865                        reportname,
866                        plot_quantity,
867                        generate_fig,
868                        surface,
869                        time_min,
870                        time_max,
871                        time_thinning,                       
872                        time_unit,
873                        title_on,
874                        use_cache,
875                        verbose)
876    return k
877
878
879##
880# @brief Read a .sww file and plot the time series.
881# @param swwfiles Dictionary of .sww files.
882# @param gauge_filename Name of gauge file.
883# @param production_dirs ??
884# @param report If True, write figures to report directory.
885# @param reportname Name of generated report (if any).
886# @param plot_quantity List containing quantities to plot.
887# @param generate_fig If True, generate figures as well as CSV files.
888# @param surface If True, then generate solution surface with 3d plot.
889# @param time_min Beginning of user defined time range for plotting purposes.
890# @param time_max End of user defined time range for plotting purposes.
891# @param time_thinning ??
892# @param time_unit ??
893# @param title_on If True, export standard graphics with title.
894# @param use_cache If True, use caching.
895# @param verbose If True, this function is verbose.
896def _sww2timeseries(swwfiles,
897                    gauge_filename,
898                    production_dirs,
899                    report = None,
900                    reportname = None,
901                    plot_quantity = None,
902                    generate_fig = False,
903                    surface = None,
904                    time_min = None,
905                    time_max = None,
906                    time_thinning = 1,                   
907                    time_unit = None,
908                    title_on = None,
909                    use_cache = False,
910                    verbose = False):   
911       
912    assert type(gauge_filename) == type(''), 'Gauge filename must be a string'
913   
914    try:
915        fid = open(gauge_filename)
916    except Exception, e:
917        msg = 'File "%s" could not be opened: Error="%s"' % (gauge_filename, e)
918        raise msg
919
920    if report is None:
921        report = False
922       
923    if plot_quantity is None:
924        plot_quantity = ['depth', 'speed']
925    else:
926        assert type(plot_quantity) == list, 'plot_quantity must be a list'
927        check_list(plot_quantity)
928
929    if surface is None:
930        surface = False
931
932    if time_unit is None:
933        time_unit = 'hours'
934   
935    if title_on is None:
936        title_on = True
937   
938    if verbose: print '\n Gauges obtained from: %s \n' %gauge_filename
939
940    gauges, locations, elev = get_gauges_from_file(gauge_filename)
941
942    sww_quantity = ['stage', 'elevation', 'xmomentum', 'ymomentum']
943
944    file_loc = []
945    f_list = []
946    label_id = []
947    leg_label = []
948    themaxT = 0.0
949    theminT = 0.0
950
951    for swwfile in swwfiles.keys():
952        try:
953            fid = open(swwfile)
954        except Exception, e:
955            msg = 'File "%s" could not be opened: Error="%s"' % (swwfile, e)
956            raise msg
957
958        print 'swwfile', swwfile
959
960        # Extract parent dir name and use as label
961        path, _ = os.path.split(swwfile)
962        _, label = os.path.split(path)       
963       
964        #print 'label', label
965        leg_label.append(label)
966
967        f = file_function(swwfile,
968                          quantities = sww_quantity,
969                          interpolation_points = gauges,
970                          time_thinning = time_thinning,
971                          verbose = verbose,
972                          use_cache = use_cache)
973
974        # determine which gauges are contained in sww file
975        count = 0
976        gauge_index = []
977        for k, g in enumerate(gauges):
978            if f(0.0, point_id = k)[2] > 1.0e6:
979                count += 1
980                if count == 1: print 'Gauges not contained here:'
981                print locations[k]
982            else:
983                gauge_index.append(k)
984
985        if len(gauge_index) > 0:
986            print 'Gauges contained here: \n',
987        else:
988            print 'No gauges contained here. \n'
989        for i in range(len(gauge_index)):
990             print locations[gauge_index[i]]
991             
992        index = swwfile.rfind(sep)
993        file_loc.append(swwfile[:index+1])
994        label_id.append(swwfiles[swwfile])
995       
996        f_list.append(f)
997        maxT = max(f.get_time())
998        minT = min(f.get_time())
999        if maxT > themaxT: themaxT = maxT
1000        if minT > theminT: theminT = minT
1001
1002    if time_min is None:
1003        time_min = theminT # min(T)
1004    else:
1005        if time_min < theminT: # min(T):
1006            msg = 'Minimum time entered not correct - please try again'
1007            raise Exception, msg
1008
1009    if time_max is None:
1010        time_max = themaxT # max(T)
1011    else:
1012        if time_max > themaxT: # max(T):
1013            msg = 'Maximum time entered not correct - please try again'
1014            raise Exception, msg
1015
1016    if verbose and len(gauge_index) > 0:
1017         print 'Inputs OK - going to generate figures'
1018
1019    if len(gauge_index) <> 0:
1020        texfile, elev_output = \
1021            generate_figures(plot_quantity, file_loc, report, reportname,
1022                             surface, leg_label, f_list, gauges, locations,
1023                             elev, gauge_index, production_dirs, time_min,
1024                             time_max, time_unit, title_on, label_id,
1025                             generate_fig, verbose)
1026    else:
1027        texfile = ''
1028        elev_output = []
1029
1030    return texfile, elev_output
1031
1032
1033##
1034# @brief Read gauge info from a file.
1035# @param filename The name of the file to read.
1036# @return A (gauges, gaugelocation, elev) tuple.
1037def get_gauges_from_file(filename):
1038    """ Read in gauge information from file
1039    """
1040
1041    from os import sep, getcwd, access, F_OK, mkdir
1042
1043    # Get data from the gauge file
1044    fid = open(filename)
1045    lines = fid.readlines()
1046    fid.close()
1047   
1048    gauges = []
1049    gaugelocation = []
1050    elev = []
1051
1052    # Check header information   
1053    line1 = lines[0]
1054    line11 = line1.split(',')
1055
1056    if isinstance(line11[0], str) is True:
1057        # We have found text in the first line
1058        east_index = None
1059        north_index = None
1060        name_index = None
1061        elev_index = None
1062
1063        for i in range(len(line11)):
1064            if line11[i].strip().lower() == 'easting':   east_index = i
1065            if line11[i].strip().lower() == 'northing':  north_index = i
1066            if line11[i].strip().lower() == 'name':      name_index = i
1067            if line11[i].strip().lower() == 'elevation': elev_index = i
1068
1069        if east_index < len(line11) and north_index < len(line11):
1070            pass
1071        else:
1072            msg = 'WARNING: %s does not contain correct header information' \
1073                  % filename
1074            msg += 'The header must be: easting, northing, name, elevation'
1075            raise Exception, msg
1076
1077        if elev_index is None: 
1078            raise Exception
1079   
1080        if name_index is None: 
1081            raise Exception
1082
1083        lines = lines[1:] # Remove header from data
1084    else:
1085        # No header, assume that this is a simple easting, northing file
1086
1087        msg = 'There was no header in file %s and the number of columns is %d' \
1088              % (filename, len(line11))
1089        msg += '- was assuming two columns corresponding to Easting and Northing'
1090        assert len(line11) == 2, msg
1091
1092        east_index = 0
1093        north_index = 1
1094
1095        N = len(lines)
1096        elev = [-9999]*N
1097        gaugelocation = range(N)
1098       
1099    # Read in gauge data
1100    for line in lines:
1101        fields = line.split(',')
1102
1103        gauges.append([float(fields[east_index]), float(fields[north_index])])
1104
1105        if len(fields) > 2:
1106            elev.append(float(fields[elev_index]))
1107            loc = fields[name_index]
1108            gaugelocation.append(loc.strip('\n'))
1109
1110    return gauges, gaugelocation, elev
1111
1112
1113##
1114# @brief Check that input quantities in quantity list are legal.
1115# @param quantity Quantity list to check.
1116# @note Raises an exception of list is not legal.
1117def check_list(quantity):
1118    """ Check that input quantities in quantity list are possible
1119    """
1120    import sys
1121    from sets import Set as set
1122
1123    all_quantity = ['stage', 'depth', 'momentum', 'xmomentum',
1124                    'ymomentum', 'speed', 'bearing', 'elevation']
1125
1126    # convert all quanitiy names to lowercase
1127    for i,j in enumerate(quantity):
1128        quantity[i] = quantity[i].lower()
1129
1130    # check that all names in 'quantity' appear in 'all_quantity'
1131    p = list(set(quantity).difference(set(all_quantity)))
1132    if len(p) != 0:
1133        msg = 'Quantities %s do not exist - please try again' %p
1134        raise Exception, msg
1135
1136
1137##
1138# @brief Calculate velocity bearing from North.
1139# @param uh ??
1140# @param vh ??
1141# @return The calculated bearing.
1142def calc_bearing(uh, vh):
1143    """ Calculate velocity bearing from North
1144    """
1145    #FIXME (Ole): I reckon we should refactor this one to use
1146    #             the function angle() in utilities/numerical_tools
1147    #
1148    #             It will be a simple matter of
1149    # * converting from radians to degrees
1150    # * moving the reference direction from [1,0] to North
1151    # * changing from counter clockwise to clocwise.
1152       
1153    angle = degrees(atan(vh/(uh+1.e-15)))
1154
1155    if (0 < angle < 90.0):
1156        if vh > 0:
1157            bearing = 90.0 - abs(angle)
1158        if vh < 0:
1159            bearing = 270.0 - abs(angle)
1160   
1161    if (-90 < angle < 0):
1162        if vh < 0:
1163            bearing = 90.0 - (angle)
1164        if vh > 0:
1165            bearing = 270.0 - (angle)
1166    if angle == 0: bearing = 0.0
1167
1168    return bearing
1169
1170
1171##
1172# @brief Generate figures from quantities and gauges for each sww file.
1173# @param plot_quantity  ??
1174# @param file_loc ??
1175# @param report ??
1176# @param reportname ??
1177# @param surface ??
1178# @param leg_label ??
1179# @param f_list ??
1180# @param gauges ??
1181# @param locations ??
1182# @param elev ??
1183# @param gauge_index ??
1184# @param production_dirs ??
1185# @param time_min ??
1186# @param time_max ??
1187# @param time_unit ??
1188# @param title_on ??
1189# @param label_id ??
1190# @param generate_fig ??
1191# @param verbose??
1192# @return (texfile2, elev_output)
1193def generate_figures(plot_quantity, file_loc, report, reportname, surface,
1194                     leg_label, f_list, gauges, locations, elev, gauge_index,
1195                     production_dirs, time_min, time_max, time_unit,
1196                     title_on, label_id, generate_fig, verbose):
1197    """ Generate figures based on required quantities and gauges for
1198    each sww file
1199    """
1200    from os import sep, altsep, getcwd, mkdir, access, F_OK, environ
1201
1202    if generate_fig is True:
1203        from pylab import ion, hold, plot, axis, figure, legend, savefig, \
1204             xlabel, ylabel, title, close, subplot
1205   
1206        if surface is True:
1207            import pylab as p1
1208            import mpl3d.mplot3d as p3
1209       
1210    if report == True:   
1211        texdir = getcwd()+sep+'report'+sep
1212        if access(texdir,F_OK) == 0:
1213            mkdir (texdir)
1214        if len(label_id) == 1:
1215            label_id1 = label_id[0].replace(sep,'')
1216            label_id2 = label_id1.replace('_','')
1217            texfile = texdir + reportname + '%s' % label_id2
1218            texfile2 = reportname + '%s' % label_id2
1219            texfilename = texfile + '.tex'
1220            fid = open(texfilename, 'w')
1221
1222            if verbose: print '\n Latex output printed to %s \n' %texfilename
1223        else:
1224            texfile = texdir+reportname
1225            texfile2 = reportname
1226            texfilename = texfile + '.tex' 
1227            fid = open(texfilename, 'w')
1228
1229            if verbose: print '\n Latex output printed to %s \n' %texfilename
1230    else:
1231        texfile = ''
1232        texfile2 = ''
1233
1234    p = len(f_list)
1235    n = []
1236    n0 = 0
1237    for i in range(len(f_list)):
1238        n.append(len(f_list[i].get_time()))
1239        if n[i] > n0: n0 = n[i] 
1240    n0 = int(n0)
1241    m = len(locations)
1242    model_time = num.zeros((n0, m, p), num.Float) 
1243    stages = num.zeros((n0, m, p), num.Float)
1244    elevations = num.zeros((n0, m, p), num.Float) 
1245    momenta = num.zeros((n0, m, p), num.Float)
1246    xmom = num.zeros((n0, m, p), num.Float)
1247    ymom = num.zeros((n0, m, p), num.Float)
1248    speed = num.zeros((n0, m, p), num.Float)
1249    bearings = num.zeros((n0, m, p), num.Float)
1250    due_east = 90.0*num.ones((n0, 1), num.Float)
1251    due_west = 270.0*num.ones((n0, 1), num.Float)
1252    depths = num.zeros((n0, m, p), num.Float)
1253    eastings = num.zeros((n0, m, p), num.Float)
1254    min_stages = []
1255    max_stages = []
1256    min_momentums = []   
1257    max_momentums = []
1258    max_xmomentums = []
1259    max_ymomentums = []
1260    min_xmomentums = []
1261    min_ymomentums = []
1262    max_speeds = []
1263    min_speeds = []   
1264    max_depths = []
1265    model_time_plot3d = num.zeros((n0, m), num.Float)
1266    stages_plot3d = num.zeros((n0, m), num.Float)
1267    eastings_plot3d = num.zeros((n0, m),num.Float)
1268    if time_unit is 'mins': scale = 60.0
1269    if time_unit is 'hours': scale = 3600.0
1270
1271    ##### loop over each swwfile #####
1272    for j, f in enumerate(f_list):
1273        if verbose: print 'swwfile %d of %d' % (j, len(f_list))
1274
1275        starttime = f.starttime
1276        comparefile = file_loc[j] + sep + 'gauges_maxmins' + '.csv'
1277        fid_compare = open(comparefile, 'w')
1278        file0 = file_loc[j] + 'gauges_t0.csv'
1279        fid_0 = open(file0, 'w')
1280
1281        ##### loop over each gauge #####
1282        for k in gauge_index:
1283            if verbose: print 'Gauge %d of %d' % (k, len(gauges))
1284
1285            g = gauges[k]
1286            min_stage = 10
1287            max_stage = 0
1288            max_momentum = max_xmomentum = max_ymomentum = 0
1289            min_momentum = min_xmomentum = min_ymomentum = 100
1290            max_speed = 0
1291            min_speed = 0           
1292            max_depth = 0           
1293            gaugeloc = str(locations[k])
1294            thisfile = file_loc[j] + sep + 'gauges_time_series' + '_' \
1295                       + gaugeloc + '.csv'
1296            fid_out = open(thisfile, 'w')
1297            s = 'Time, Stage, Momentum, Speed, Elevation, xmom, ymom, Bearing \n'
1298            fid_out.write(s)
1299
1300            #### generate quantities #######
1301            for i, t in enumerate(f.get_time()):
1302                if time_min <= t <= time_max:
1303                    w = f(t, point_id = k)[0]
1304                    z = f(t, point_id = k)[1]
1305                    uh = f(t, point_id = k)[2]
1306                    vh = f(t, point_id = k)[3]
1307                    depth = w-z     
1308                    m = sqrt(uh*uh + vh*vh)
1309                    if depth < 0.001:
1310                        vel = 0.0
1311                    else:
1312                        vel = m / (depth + 1.e-6/depth) 
1313                    bearing = calc_bearing(uh, vh)                   
1314                    model_time[i,k,j] = (t + starttime)/scale #t/60.0
1315                    stages[i,k,j] = w
1316                    elevations[i,k,j] = z
1317                    xmom[i,k,j] = uh
1318                    ymom[i,k,j] = vh
1319                    momenta[i,k,j] = m
1320                    speed[i,k,j] = vel
1321                    bearings[i,k,j] = bearing
1322                    depths[i,k,j] = depth
1323                    thisgauge = gauges[k]
1324                    eastings[i,k,j] = thisgauge[0]
1325                    s = '%.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f,\n' \
1326                            % (t, w, m, vel, z, uh, vh, bearing)
1327                    fid_out.write(s)
1328                    if t == 0:
1329                        s = '%.2f, %.2f, %.2f\n' % (g[0], g[1], w)
1330                        fid_0.write(s)
1331                    if t/60.0 <= 13920: tindex = i
1332                    if w > max_stage: max_stage = w
1333                    if w < min_stage: min_stage = w
1334                    if m > max_momentum: max_momentum = m
1335                    if m < min_momentum: min_momentum = m                   
1336                    if uh > max_xmomentum: max_xmomentum = uh
1337                    if vh > max_ymomentum: max_ymomentum = vh
1338                    if uh < min_xmomentum: min_xmomentum = uh
1339                    if vh < min_ymomentum: min_ymomentum = vh
1340                    if vel > max_speed: max_speed = vel
1341                    if vel < min_speed: min_speed = vel                   
1342                    if z > 0 and depth > max_depth: max_depth = depth
1343                   
1344                   
1345            s = '%.2f, %.2f, %.2f, %.2f, %s\n' \
1346                    % (max_stage, min_stage, z, thisgauge[0], leg_label[j])
1347            fid_compare.write(s)
1348            max_stages.append(max_stage)
1349            min_stages.append(min_stage)
1350            max_momentums.append(max_momentum)
1351            max_xmomentums.append(max_xmomentum)
1352            max_ymomentums.append(max_ymomentum)
1353            min_xmomentums.append(min_xmomentum)
1354            min_ymomentums.append(min_ymomentum)
1355            min_momentums.append(min_momentum)           
1356            max_depths.append(max_depth)
1357            max_speeds.append(max_speed)
1358            min_speeds.append(min_speed)           
1359            #### finished generating quantities for each swwfile #####
1360       
1361        model_time_plot3d[:,:] = model_time[:,:,j]
1362        stages_plot3d[:,:] = stages[:,:,j]
1363        eastings_plot3d[:,] = eastings[:,:,j]
1364           
1365        if surface is True:
1366            print 'Printing surface figure'
1367            for i in range(2):
1368                fig = p1.figure(10)
1369                ax = p3.Axes3D(fig)
1370                if len(gauges) > 80:
1371                    ax.plot_surface(model_time[:,:,j],
1372                                    eastings[:,:,j],
1373                                    stages[:,:,j])
1374                else:
1375                    ax.plot3D(num.ravel(eastings[:,:,j]),
1376                              num.ravel(model_time[:,:,j]),
1377                              num.ravel(stages[:,:,j]))
1378                ax.set_xlabel('time')
1379                ax.set_ylabel('x')
1380                ax.set_zlabel('stage')
1381                fig.add_axes(ax)
1382                p1.show()
1383                surfacefig = 'solution_surface%s' % leg_label[j]
1384                p1.savefig(surfacefig)
1385                p1.close()
1386           
1387    #### finished generating quantities for all swwfiles #####
1388
1389    # x profile for given time
1390    if surface is True:
1391        figure(11)
1392        plot(eastings[tindex,:,j], stages[tindex,:,j])
1393        xlabel('x')
1394        ylabel('stage')
1395        profilefig = 'solution_xprofile' 
1396        savefig('profilefig')
1397
1398    elev_output = []
1399    if generate_fig is True:
1400        depth_axis = axis([starttime/scale, time_max/scale, -0.1,
1401                           max(max_depths)*1.1])
1402        stage_axis = axis([starttime/scale, time_max/scale,
1403                           min(min_stages), max(max_stages)*1.1])
1404        vel_axis = axis([starttime/scale, time_max/scale,
1405                         min(min_speeds), max(max_speeds)*1.1])
1406        mom_axis = axis([starttime/scale, time_max/scale,
1407                         min(min_momentums), max(max_momentums)*1.1])
1408        xmom_axis = axis([starttime/scale, time_max/scale,
1409                          min(min_xmomentums), max(max_xmomentums)*1.1])
1410        ymom_axis = axis([starttime/scale, time_max/scale,
1411                          min(min_ymomentums), max(max_ymomentums)*1.1])
1412        cstr = ['g', 'r', 'b', 'c', 'm', 'y', 'k']
1413        nn = len(plot_quantity)
1414        no_cols = 2
1415       
1416        if len(label_id) > 1: graphname_report = []
1417        pp = 1
1418        div = 11.
1419        cc = 0
1420        for k in gauge_index:
1421            g = gauges[k]
1422            count1 = 0
1423            if report == True and len(label_id) > 1:
1424                s = '\\begin{figure}[ht] \n' \
1425                    '\\centering \n' \
1426                    '\\begin{tabular}{cc} \n'
1427                fid.write(s)
1428            if len(label_id) > 1: graphname_report = []
1429
1430            #### generate figures for each gauge ####
1431            for j, f in enumerate(f_list):
1432                ion()
1433                hold(True)
1434                count = 0
1435                where1 = 0
1436                where2 = 0
1437                word_quantity = ''
1438                if report == True and len(label_id) == 1:
1439                    s = '\\begin{figure}[hbt] \n' \
1440                        '\\centering \n' \
1441                        '\\begin{tabular}{cc} \n'
1442                    fid.write(s)
1443                   
1444                for which_quantity in plot_quantity:
1445                    count += 1
1446                    where1 += 1
1447                    figure(count, frameon = False)
1448                    if which_quantity == 'depth':
1449                        plot(model_time[0:n[j]-1,k,j],
1450                             depths[0:n[j]-1,k,j], '-', c = cstr[j])
1451                        units = 'm'
1452                        axis(depth_axis)
1453                    if which_quantity == 'stage':
1454                        if elevations[0,k,j] <= 0:
1455                            plot(model_time[0:n[j]-1,k,j],
1456                                 stages[0:n[j]-1,k,j], '-', c = cstr[j])
1457                            axis(stage_axis)
1458                        else:
1459                            plot(model_time[0:n[j]-1,k,j],
1460                                 depths[0:n[j]-1,k,j], '-', c = cstr[j])
1461                            #axis(depth_axis)                 
1462                        units = 'm'
1463                    if which_quantity == 'momentum':
1464                        plot(model_time[0:n[j]-1,k,j],
1465                             momenta[0:n[j]-1,k,j], '-', c = cstr[j])
1466                        axis(mom_axis)
1467                        units = 'm^2 / sec'
1468                    if which_quantity == 'xmomentum':
1469                        plot(model_time[0:n[j]-1,k,j],
1470                             xmom[0:n[j]-1,k,j], '-', c = cstr[j])
1471                        axis(xmom_axis)
1472                        units = 'm^2 / sec'
1473                    if which_quantity == 'ymomentum':
1474                        plot(model_time[0:n[j]-1,k,j],
1475                             ymom[0:n[j]-1,k,j], '-', c = cstr[j])
1476                        axis(ymom_axis)
1477                        units = 'm^2 / sec'
1478                    if which_quantity == 'speed':
1479                        plot(model_time[0:n[j]-1,k,j],
1480                             speed[0:n[j]-1,k,j], '-', c = cstr[j])
1481                        axis(vel_axis)
1482                        units = 'm / sec'
1483                    if which_quantity == 'bearing':
1484                        plot(model_time[0:n[j]-1,k,j],bearings[0:n[j]-1,k,j],'-',
1485                             model_time[0:n[j]-1,k,j], due_west[0:n[j]-1], '-.', 
1486                             model_time[0:n[j]-1,k,j], due_east[0:n[j]-1], '-.')
1487                        units = 'degrees from North'
1488                        #ax = axis([time_min, time_max, 0.0, 360.0])
1489                        legend(('Bearing','West','East'))
1490
1491                    if time_unit is 'mins': xlabel('time (mins)')
1492                    if time_unit is 'hours': xlabel('time (hours)')
1493                    #if which_quantity == 'stage' \
1494                    #   and elevations[0:n[j]-1,k,j] > 0:
1495                    #    ylabel('%s (%s)' %('depth', units))
1496                    #else:
1497                    #    ylabel('%s (%s)' %(which_quantity, units))
1498                        #ylabel('%s (%s)' %('wave height', units))
1499                    ylabel('%s (%s)' %(which_quantity, units))
1500                    if len(label_id) > 1: legend((leg_label),loc='upper right')
1501
1502                    #gaugeloc1 = gaugeloc.replace(' ','')
1503                    #gaugeloc2 = gaugeloc1.replace('_','')
1504                    gaugeloc2 = str(locations[k]).replace(' ','')
1505                    graphname = '%sgauge%s_%s' %(file_loc[j],
1506                                                 gaugeloc2,
1507                                                 which_quantity)
1508
1509                    if report == True and len(label_id) > 1:
1510                        figdir = getcwd()+sep+'report_figures'+sep
1511                        if access(figdir,F_OK) == 0 :
1512                            mkdir (figdir)
1513                        latex_file_loc = figdir.replace(sep,altsep) 
1514                        # storing files in production directory   
1515                        graphname_latex = '%sgauge%s%s' \
1516                                          % (latex_file_loc, gaugeloc2,
1517                                             which_quantity)
1518                        # giving location in latex output file
1519                        graphname_report_input = '%sgauge%s%s' % \
1520                                                 ('..' + altsep + 
1521                                                      'report_figures' + altsep,
1522                                                  gaugeloc2, which_quantity)
1523                        graphname_report.append(graphname_report_input)
1524                       
1525                        # save figures in production directory for report
1526                        savefig(graphname_latex)
1527
1528                    if report == True:
1529                        figdir = getcwd() + sep + 'report_figures' + sep
1530                        if access(figdir,F_OK) == 0:
1531                            mkdir(figdir)
1532                        latex_file_loc = figdir.replace(sep,altsep)   
1533
1534                        if len(label_id) == 1: 
1535                            # storing files in production directory 
1536                            graphname_latex = '%sgauge%s%s%s' % \
1537                                              (latex_file_loc, gaugeloc2,
1538                                               which_quantity, label_id2)
1539                            # giving location in latex output file
1540                            graphname_report = '%sgauge%s%s%s' % \
1541                                               ('..' + altsep +
1542                                                    'report_figures' + altsep,
1543                                                gaugeloc2, which_quantity,
1544                                                label_id2)
1545                            s = '\includegraphics' \
1546                                '[width=0.49\linewidth, height=50mm]{%s%s}' % \
1547                                (graphname_report, '.png')
1548                            fid.write(s)
1549                            if where1 % 2 == 0:
1550                                s = '\\\\ \n'
1551                                where1 = 0
1552                            else:
1553                                s = '& \n'
1554                            fid.write(s)
1555                            savefig(graphname_latex)
1556                   
1557                    if title_on == True:
1558                        title('%s scenario: %s at %s gauge' % \
1559                              (label_id, which_quantity, gaugeloc2))
1560                        #title('Gauge %s (MOST elevation %.2f, ' \
1561                        #      'ANUGA elevation %.2f)' % \
1562                        #      (gaugeloc2, elevations[10,k,0],
1563                        #       elevations[10,k,1]))
1564
1565                    savefig(graphname) # save figures with sww file
1566
1567                if report == True and len(label_id) == 1:
1568                    for i in range(nn-1):
1569                        if nn > 2:
1570                            if plot_quantity[i] == 'stage' \
1571                               and elevations[0,k,j] > 0:
1572                                word_quantity += 'depth' + ', '
1573                            else:
1574                                word_quantity += plot_quantity[i] + ', '
1575                        else:
1576                            if plot_quantity[i] == 'stage' \
1577                               and elevations[0,k,j] > 0:
1578                                word_quantity += 'depth' + ', '
1579                            else:
1580                                word_quantity += plot_quantity[i]
1581                       
1582                    if plot_quantity[nn-1] == 'stage' and elevations[0,k,j] > 0:
1583                        word_quantity += ' and ' + 'depth'
1584                    else:
1585                        word_quantity += ' and ' + plot_quantity[nn-1]
1586                    caption = 'Time series for %s at %s location ' \
1587                              '(elevation %.2fm)' % \
1588                              (word_quantity, locations[k], elev[k])
1589                    if elev[k] == 0.0:
1590                        caption = 'Time series for %s at %s location ' \
1591                                  '(elevation %.2fm)' % \
1592                                  (word_quantity, locations[k],
1593                                   elevations[0,k,j])
1594                        east = gauges[0]
1595                        north = gauges[1]
1596                        elev_output.append([locations[k], east, north,
1597                                            elevations[0,k,j]])
1598                    label = '%sgauge%s' % (label_id2, gaugeloc2)
1599                    s = '\end{tabular} \n' \
1600                        '\\caption{%s} \n' \
1601                        '\label{fig:%s} \n' \
1602                        '\end{figure} \n \n' % (caption, label)
1603                    fid.write(s)
1604                    cc += 1
1605                    if cc % 6 == 0: fid.write('\\clearpage \n')
1606                    savefig(graphname_latex)               
1607                   
1608            if report == True and len(label_id) > 1:
1609                for i in range(nn-1):
1610                    if nn > 2:
1611                        if plot_quantity[i] == 'stage' and elevations[0,k,j] > 0:
1612                            word_quantity += 'depth' + ','
1613                        else:
1614                            word_quantity += plot_quantity[i] + ', '
1615                    else:
1616                        if plot_quantity[i] == 'stage' and elevations[0,k,j] > 0:
1617                            word_quantity += 'depth'
1618                        else:
1619                            word_quantity += plot_quantity[i]
1620                    where1 = 0
1621                    count1 += 1
1622                    index = j*len(plot_quantity)
1623                    for which_quantity in plot_quantity:
1624                        where1 += 1
1625                        s = '\includegraphics' \
1626                            '[width=0.49\linewidth, height=50mm]{%s%s}' % \
1627                            (graphname_report[index], '.png')
1628                        index += 1
1629                        fid.write(s)
1630                        if where1 % 2 == 0:
1631                            s = '\\\\ \n'
1632                            where1 = 0
1633                        else:
1634                            s = '& \n'
1635                        fid.write(s)
1636                word_quantity += ' and ' + plot_quantity[nn-1]           
1637                label = 'gauge%s' %(gaugeloc2) 
1638                caption = 'Time series for %s at %s location ' \
1639                          '(elevation %.2fm)' % \
1640                          (word_quantity, locations[k], elev[k])
1641                if elev[k] == 0.0:
1642                        caption = 'Time series for %s at %s location ' \
1643                                  '(elevation %.2fm)' % \
1644                                  (word_quantity, locations[k],
1645                                   elevations[0,k,j])
1646                        thisgauge = gauges[k]
1647                        east = thisgauge[0]
1648                        north = thisgauge[1]
1649                        elev_output.append([locations[k], east, north,
1650                                            elevations[0,k,j]])
1651                       
1652                s = '\end{tabular} \n' \
1653                    '\\caption{%s} \n' \
1654                    '\label{fig:%s} \n' \
1655                    '\end{figure} \n \n' % (caption, label)
1656                fid.write(s)
1657                if float((k+1)/div - pp) == 0.:
1658                    fid.write('\\clearpage \n')
1659                    pp += 1
1660                #### finished generating figures ###
1661
1662            close('all')
1663       
1664    return texfile2, elev_output
1665
1666
1667# FIXME (DSG): Add unit test, make general, not just 2 files,
1668# but any number of files.
1669##
1670# @brief ??
1671# @param dir_name ??
1672# @param filename1 ??
1673# @param filename2 ??
1674# @return ??
1675# @note TEMP
1676def copy_code_files(dir_name, filename1, filename2):
1677    """Temporary Interface to new location"""
1678
1679    from anuga.shallow_water.data_manager import \
1680                    copy_code_files as dm_copy_code_files
1681    print 'copy_code_files has moved from util.py.  ',
1682    print 'Please use "from anuga.shallow_water.data_manager \
1683                                        import copy_code_files"'
1684   
1685    return dm_copy_code_files(dir_name, filename1, filename2)
1686
1687
1688##
1689# @brief Create a nested sub-directory path.
1690# @param root_directory The base diretory path.
1691# @param directories An iterable of sub-directory names.
1692# @return The final joined directory path.
1693# @note If each sub-directory doesn't exist, it will be created.
1694def add_directories(root_directory, directories):
1695    """
1696    Add the first sub-directory in 'directories' to root_directory.
1697    Then add the second sub-directory to the accumulating path and so on.
1698
1699    Return the path of the final directory.
1700
1701    This is handy for specifying and creating a directory where data will go.
1702    """
1703    dir = root_directory
1704    for new_dir in directories:
1705        dir = os.path.join(dir, new_dir)
1706        if not access(dir,F_OK):
1707            mkdir(dir)
1708    return dir
1709
1710
1711##
1712# @brief
1713# @param filename
1714# @param separator_value
1715# @return
1716# @note TEMP
1717def get_data_from_file(filename, separator_value=','):
1718    """Temporary Interface to new location"""
1719    from anuga.shallow_water.data_manager import \
1720                        get_data_from_file as dm_get_data_from_file
1721    print 'get_data_from_file has moved from util.py'
1722    print 'Please use "from anuga.shallow_water.data_manager \
1723                                     import get_data_from_file"'
1724   
1725    return dm_get_data_from_file(filename,separator_value = ',')
1726
1727
1728##
1729# @brief
1730# @param verbose
1731# @param kwargs
1732# @return
1733# @note TEMP
1734def store_parameters(verbose=False,**kwargs):
1735    """Temporary Interface to new location"""
1736   
1737    from anuga.shallow_water.data_manager \
1738                    import store_parameters as dm_store_parameters
1739    print 'store_parameters has moved from util.py.'
1740    print 'Please use "from anuga.shallow_water.data_manager \
1741                                     import store_parameters"'
1742   
1743    return dm_store_parameters(verbose=False,**kwargs)
1744
1745
1746##
1747# @brief Remove vertices that are not associated with any triangle.
1748# @param verts An iterable (or array) of points.
1749# @param triangles An iterable of 3 element tuples.
1750# @param number_of_full_nodes ??
1751# @return (verts, triangles) where 'verts' has been updated.
1752def remove_lone_verts(verts, triangles, number_of_full_nodes=None):
1753    """Removes vertices that are not associated with any triangles.
1754
1755    verts is a list/array of points.
1756    triangles is a list of 3 element tuples.  Each tuple represents a triangle.
1757    number_of_full_nodes relate to parallelism when a mesh has an
1758        extra layer of ghost points.
1759    """
1760
1761    verts = ensure_numeric(verts)
1762    triangles = ensure_numeric(triangles)
1763   
1764    N = len(verts)
1765   
1766    # initialise the array to easily find the index of the first loner
1767    # ie, if N=3 -> [6,5,4]
1768    loners=num.arange(2*N, N, -1)
1769    for t in triangles:
1770        for vert in t:
1771            loners[vert]= vert # all non-loners will have loners[i]=i
1772
1773    lone_start = 2*N - max(loners) # The index of the first loner
1774
1775    if lone_start-1 == N:
1776        # no loners
1777        pass
1778    elif min(loners[lone_start:N]) > N:
1779        # All the loners are at the end of the vert array
1780        verts = verts[0:lone_start]
1781    else:
1782        # change the loners list so it can be used to modify triangles
1783        # Remove the loners from verts
1784        # Could've used X=compress(less(loners,N),loners)
1785        # verts=num.take(verts,X)  to Remove the loners from verts
1786        # but I think it would use more memory
1787        new_i = lone_start      # point at first loner - 'shuffle down' target
1788        for i in range(lone_start, N):
1789            if loners[i] >= N:  # [i] is a loner, leave alone
1790                pass
1791            else:               # a non-loner, move down
1792                loners[i] = new_i
1793                verts[new_i] = verts[i]
1794                new_i += 1
1795        verts = verts[0:new_i]
1796
1797        # Modify the triangles
1798        #print "loners", loners
1799        #print "triangles before", triangles
1800        triangles = num.choose(triangles,loners)
1801        #print "triangles after", triangles
1802    return verts, triangles
1803
1804
1805##
1806# @brief Compute centroid values from vertex values
1807# @param x Values at vertices of triangular mesh.
1808# @param triangles Nx3 integer array pointing to vertex information.
1809# @return [N] array of centroid values.
1810def get_centroid_values(x, triangles):
1811    """Compute centroid values from vertex values
1812   
1813    x: Values at vertices of triangular mesh
1814    triangles: Nx3 integer array pointing to vertex information
1815    for each of the N triangels. Elements of triangles are
1816    indices into x
1817    """
1818       
1819    xc = num.zeros(triangles.shape[0], num.Float) # Space for centroid info
1820   
1821    for k in range(triangles.shape[0]):
1822        # Indices of vertices
1823        i0 = triangles[k][0]
1824        i1 = triangles[k][1]
1825        i2 = triangles[k][2]       
1826       
1827        xc[k] = (x[i0] + x[i1] + x[i2])/3
1828
1829    return xc
1830
1831
1832# @note TEMP
1833def make_plots_from_csv_file(directories_dic={dir:['gauge', 0, 0]},
1834                                output_dir='',
1835                                base_name='',
1836                                plot_numbers=['3-5'],
1837                                quantities=['speed','stage','momentum'],
1838                                assess_all_csv_files=True,
1839                                extra_plot_name='test'):
1840
1841    msg = 'make_plots_from_csv_file has been replaced by csv2timeseries_graphs '
1842    msg += 'Please use "from anuga.abstract_2d_finite_volumes.util import ' \
1843           'csv2timeseries_graphs"'
1844    raise Exception, msg
1845
1846    return csv2timeseries_graphs(directories_dic,
1847                                 output_dir,
1848                                 base_name,
1849                                 plot_numbers,
1850                                 quantities,
1851                                 extra_plot_name,
1852                                 assess_all_csv_files)
1853
1854
1855##
1856# @brief Plot time series from CSV files.
1857# @param directories_dic
1858# @param output_dir
1859# @param base_name
1860# @param plot_numbers
1861# @param quantities
1862# @param extra_plot_name
1863# @param assess_all_csv_files
1864# @param create_latex
1865# @param verbose
1866# @note Assumes that 'elevation' is in the CSV file(s).
1867def csv2timeseries_graphs(directories_dic={},
1868                          output_dir='',
1869                          base_name=None,
1870                          plot_numbers='',
1871                          quantities=['stage'],
1872                          extra_plot_name='',
1873                          assess_all_csv_files=True,
1874                          create_latex=False,
1875                          verbose=False):
1876                               
1877    """
1878    Read in csv files that have the right header information and
1879    plot time series such as Stage, Speed, etc. Will also plot several
1880    time series on one plot. Filenames must follow this convention,
1881    <base_name><plot_number>.csv eg gauge_timeseries3.csv
1882   
1883    NOTE: relies that 'elevation' is in the csv file!
1884
1885    Each file represents a location and within each file there are
1886    time, quantity columns.
1887   
1888    For example:   
1889    if "directories_dic" defines 4 directories and in each directories
1890    there is a csv files corresponding to the right "plot_numbers",
1891    this will create a plot with 4 lines one for each directory AND
1892    one plot for each "quantities".  ??? FIXME: unclear.
1893   
1894    Usage:
1895        csv2timeseries_graphs(directories_dic={'slide'+sep:['Slide',0, 0],
1896                                       'fixed_wave'+sep:['Fixed Wave',0,0]},
1897                            output_dir='fixed_wave'+sep,
1898                            base_name='gauge_timeseries_',
1899                            plot_numbers='',
1900                            quantities=['stage','speed'],
1901                            extra_plot_name='',
1902                            assess_all_csv_files=True,                           
1903                            create_latex=False,
1904                            verbose=True)
1905            this will create one plot for stage with both 'slide' and
1906            'fixed_wave' lines on it for stage and speed for each csv
1907            file with 'gauge_timeseries_' as the prefix. The graghs
1908            will be in the output directory 'fixed_wave' and the graph
1909            axis will be determined by assessing all the
1910   
1911    ANOTHER EXAMPLE
1912        new_csv2timeseries_graphs(directories_dic={'slide'+sep:['Slide',0, 0],
1913                                       'fixed_wave'+sep:['Fixed Wave',0,0]},
1914                            output_dir='fixed_wave'+sep,
1915                            base_name='gauge_timeseries_',
1916                            plot_numbers=['1-3'],
1917                            quantities=['stage','speed'],
1918                            extra_plot_name='',
1919                            assess_all_csv_files=False,                           
1920                            create_latex=False,
1921                            verbose=True)
1922        This will plot csv files called gauge_timeseries_1.csv and
1923        gauge_timeseries3.csv from both 'slide' and 'fixed_wave' directories
1924        to 'fixed_wave'. There will be 4 plots created two speed and two stage
1925        one for each csv file. There will be two lines on each of these plots.
1926        And the axis will have been determined from only these files, had
1927        assess_all_csv_files = True all csv file with 'gauges_timeseries_' prefix
1928        would of been assessed.
1929   
1930    ANOTHER EXAMPLE   
1931         csv2timeseries_graphs({'J:'+sep+'anuga_validation'+sep:['new',20,-.1],
1932                                   'J:'+sep+'conical_island'+sep:['test',0,0]},
1933                                   output_dir='',
1934                                   plot_numbers=['1','3'],
1935                                   quantities=['stage','depth','bearing'],
1936                                   base_name='gauge_b',
1937                                   assess_all_csv_files=True,
1938                                  verbose=True)   
1939       
1940            This will produce one plot for each quantity (therefore 3) in the
1941            current directory, each plot will have 2 lines on them. The first
1942            plot named 'new' will have the time offseted by 20secs and the stage
1943            height adjusted by -0.1m
1944       
1945    Inputs:
1946        directories_dic: dictionary of directory with values (plot
1947                         legend name for directory), (start time of
1948                         the time series) and the (value to add to
1949                         stage if needed). For example
1950                         {dir1:['Anuga_ons',5000, 0],
1951                          dir2:['b_emoth',5000,1.5],
1952                          dir3:['b_ons',5000,1.5]}
1953                         Having multiple directories defined will plot them on
1954                         one plot, therefore there will be 3 lines on each of
1955                         these plot. If you only want one line per plot call
1956                         csv2timeseries_graph separately for each directory,
1957                         eg only have one directory in the 'directories_dic' in
1958                         each call.
1959                         
1960        output_dir: directory for the plot outputs. Only important to define when
1961                    you have more than one directory in your directories_dic, if
1962                    you have not defined it and you have multiple directories in
1963                    'directories_dic' there will be plots in each directory,
1964                    however only one directory will contain the complete
1965                    plot/graphs.
1966       
1967        base_name: Is used a couple of times.
1968                   1) to find the csv files to be plotted if there is no
1969                      'plot_numbers' then csv files with 'base_name' are plotted
1970                   2) in the title of the plots, the length of base_name is
1971                      removed from the front of the filename to be used in the
1972                      title.
1973                   This could be changed if needed.
1974                   Note is ignored if assess_all_csv_files=True
1975       
1976        plot_numbers: a String list of numbers to plot. For example
1977                      [0-4,10,15-17] will read and attempt to plot
1978                      the follow 0,1,2,3,4,10,15,16,17
1979                      NOTE: if no plot numbers this will create one plot per
1980                            quantity, per gauge
1981
1982        quantities: Will get available quantities from the header in the csv
1983                    file.  Quantities must be one of these.
1984                    NOTE: ALL QUANTITY NAMES MUST BE lower case!
1985                   
1986        extra_plot_name: A string that is appended to the end of the
1987                         output filename.
1988                   
1989        assess_all_csv_files: if true it will read ALL csv file with
1990                             "base_name", regardless of 'plot_numbers'
1991                              and determine a uniform set of axes for
1992                              Stage, Speed and Momentum. IF FALSE it
1993                              will only read the csv file within the
1994                             'plot_numbers'
1995                             
1996        create_latex: NOT IMPLEMENTED YET!! sorry Jane....
1997       
1998    OUTPUTS: saves the plots to
1999              <output_dir><base_name><plot_number><extra_plot_name>.png
2000    """
2001
2002    try: 
2003        import pylab
2004    except ImportError:
2005        msg='csv2timeseries_graphs needs pylab to be installed correctly'
2006        raise msg
2007            #ANUGA don't need pylab to work so the system doesn't
2008            #rely on pylab being installed
2009        return
2010
2011    from os import sep
2012    from anuga.shallow_water.data_manager import \
2013                               get_all_files_with_extension, csv2dict
2014
2015    seconds_in_hour = 3600
2016    seconds_in_minutes = 60
2017   
2018    quantities_label={}
2019#    quantities_label['time'] = 'time (hours)'
2020    quantities_label['time'] = 'time (minutes)'
2021    quantities_label['stage'] = 'wave height (m)'
2022    quantities_label['speed'] = 'speed (m/s)'
2023    quantities_label['momentum'] = 'momentum (m^2/sec)'
2024    quantities_label['depth'] = 'water depth (m)'
2025    quantities_label['xmomentum'] = 'momentum (m^2/sec)'
2026    quantities_label['ymomentum'] = 'momentum (m^2/sec)'
2027    quantities_label['bearing'] = 'degrees (o)'
2028    quantities_label['elevation'] = 'elevation (m)'
2029   
2030    if extra_plot_name != '':
2031        extra_plot_name = '_' + extra_plot_name
2032
2033    new_plot_numbers=[]
2034    #change plot_numbers to list, eg ['0-4','10']
2035    #to ['0','1','2','3','4','10']
2036    for i, num_string in enumerate(plot_numbers):
2037        if '-' in num_string: 
2038            start = int(num_string[:num_string.rfind('-')])
2039            end = int(num_string[num_string.rfind('-') + 1:]) + 1
2040            for x in range(start, end):
2041                new_plot_numbers.append(str(x))
2042        else:
2043            new_plot_numbers.append(num_string)
2044
2045    #finds all the files that fit the specs provided and return a list of them
2046    #so to help find a uniform max and min for the plots...
2047    list_filenames=[]
2048    all_csv_filenames=[]
2049    if verbose: print 'Determining files to access for axes ranges.'
2050   
2051    for i,directory in enumerate(directories_dic.keys()):
2052        all_csv_filenames.append(get_all_files_with_extension(directory,
2053                                                              base_name, '.csv'))
2054
2055        filenames=[]
2056        if plot_numbers == '': 
2057            list_filenames.append(get_all_files_with_extension(directory,
2058                                                               base_name,'.csv'))
2059        else:
2060            for number in new_plot_numbers:
2061                filenames.append(base_name + number)
2062            list_filenames.append(filenames)
2063
2064    #use all the files to get the values for the plot axis
2065    max_start_time= -1000.
2066    min_start_time = 100000 
2067   
2068    if verbose: print 'Determining uniform axes' 
2069
2070    #this entire loop is to determine the min and max range for the
2071    #axes of the plots
2072
2073#    quantities.insert(0,'elevation')
2074    quantities.insert(0,'time')
2075
2076    directory_quantity_value={}
2077#    quantity_value={}
2078    min_quantity_value={}
2079    max_quantity_value={}
2080
2081    for i, directory in enumerate(directories_dic.keys()):
2082        filename_quantity_value = {}
2083        if assess_all_csv_files == False:
2084            which_csv_to_assess = list_filenames[i]
2085        else:
2086            #gets list of filenames for directory "i"
2087            which_csv_to_assess = all_csv_filenames[i]
2088       
2089        for j, filename in enumerate(which_csv_to_assess):
2090            quantity_value = {}
2091
2092            dir_filename = join(directory,filename)
2093            attribute_dic, title_index_dic = csv2dict(dir_filename + '.csv')
2094            directory_start_time = directories_dic[directory][1]
2095            directory_add_tide = directories_dic[directory][2]
2096
2097            if verbose: print 'reading: %s.csv' %dir_filename
2098
2099            #add time to get values
2100            for k, quantity in enumerate(quantities):
2101                quantity_value[quantity] = [float(x) for
2102                                                x in attribute_dic[quantity]]
2103
2104                #add tide to stage if provided
2105                if quantity == 'stage':
2106                     quantity_value[quantity] = num.array(quantity_value[quantity], num.Float) \
2107                                                          + directory_add_tide
2108
2109                #condition to find max and mins for all the plots
2110                # populate the list with something when i=0 and j=0 and
2111                # then compare to the other values to determine abs max and min
2112                if i==0 and j==0:
2113                    min_quantity_value[quantity], \
2114                        max_quantity_value[quantity] = \
2115                            get_min_max_values(quantity_value[quantity])
2116
2117                    if quantity != 'time':
2118                        min_quantity_value[quantity] = \
2119                            min_quantity_value[quantity] *1.1
2120                        max_quantity_value[quantity] = \
2121                            max_quantity_value[quantity] *1.1
2122                else:
2123                    min, max = get_min_max_values(quantity_value[quantity])
2124               
2125                    # min and max are multipled by "1+increase_axis" to get axes
2126                    # that are slighty bigger than the max and mins
2127                    # so the plots look good.
2128
2129                    increase_axis = (max-min)*0.05
2130                    if min <= min_quantity_value[quantity]:
2131                        if quantity == 'time': 
2132                            min_quantity_value[quantity] = min
2133                        else:
2134                            if round(min,2) == 0.00:
2135                                min_quantity_value[quantity] = -increase_axis
2136#                                min_quantity_value[quantity] = -2.
2137                                #min_quantity_value[quantity] = \
2138                                #    -max_quantity_value[quantity]*increase_axis
2139                            else:
2140#                                min_quantity_value[quantity] = \
2141#                                    min*(1+increase_axis)
2142                                min_quantity_value[quantity]=min-increase_axis
2143                   
2144                    if max > max_quantity_value[quantity]: 
2145                        if quantity == 'time': 
2146                            max_quantity_value[quantity] = max
2147                        else:
2148                            max_quantity_value[quantity] = max + increase_axis
2149#                            max_quantity_value[quantity]=max*(1+increase_axis)
2150
2151            #set the time... ???
2152            if min_start_time > directory_start_time: 
2153                min_start_time = directory_start_time
2154            if max_start_time < directory_start_time: 
2155                max_start_time = directory_start_time
2156           
2157            filename_quantity_value[filename]=quantity_value
2158           
2159        directory_quantity_value[directory]=filename_quantity_value
2160   
2161    #final step to unifrom axis for the graphs
2162    quantities_axis={}
2163   
2164    for i, quantity in enumerate(quantities):
2165        quantities_axis[quantity] = (float(min_start_time) \
2166                                         / float(seconds_in_minutes),
2167                                     (float(max_quantity_value['time']) \
2168                                          + float(max_start_time)) \
2169                                              / float(seconds_in_minutes),
2170                                     min_quantity_value[quantity],
2171                                     max_quantity_value[quantity])
2172
2173        if verbose and (quantity != 'time' and quantity != 'elevation'): 
2174            print 'axis for quantity %s are x:(%s to %s)%s and y:(%s to %s)%s' \
2175                  % (quantity, 
2176                     quantities_axis[quantity][0],
2177                     quantities_axis[quantity][1],
2178                     quantities_label['time'],
2179                     quantities_axis[quantity][2],
2180                     quantities_axis[quantity][3],
2181                     quantities_label[quantity])
2182
2183    cstr = ['b', 'r', 'g', 'c', 'm', 'y', 'k']
2184
2185    if verbose: print 'Now start to plot \n'
2186   
2187    i_max = len(directories_dic.keys())
2188    legend_list_dic = {}
2189    legend_list = []
2190    for i, directory in enumerate(directories_dic.keys()):
2191        if verbose: print 'Plotting in %s %s' % (directory, new_plot_numbers)
2192
2193        # FIXME THIS SORT IS VERY IMPORTANT
2194        # Without it the assigned plot numbers may not work correctly
2195        # there must be a better way
2196        list_filenames[i].sort()
2197        for j, filename in enumerate(list_filenames[i]):
2198            if verbose: print 'Starting %s' % filename 
2199
2200            directory_name = directories_dic[directory][0]
2201            directory_start_time = directories_dic[directory][1]
2202            directory_add_tide = directories_dic[directory][2]
2203           
2204            # create an if about the start time and tide height if don't exist
2205            attribute_dic, title_index_dic = csv2dict(directory + sep
2206                                                      + filename + '.csv')
2207            #get data from dict in to list
2208            #do maths to list by changing to array
2209            t = (num.array(directory_quantity_value[directory][filename]['time'])
2210                     + directory_start_time) / seconds_in_minutes
2211
2212            #finds the maximum elevation, used only as a test
2213            # and as info in the graphs
2214            max_ele=-100000
2215            min_ele=100000
2216            elevation = [float(x) for x in attribute_dic["elevation"]]
2217           
2218            min_ele, max_ele = get_min_max_values(elevation)
2219           
2220            if min_ele != max_ele:
2221                print "Note! Elevation changes in %s" %dir_filename
2222
2223            # creates a dictionary with keys that is the filename and attributes
2224            # are a list of lists containing 'directory_name' and 'elevation'.
2225            # This is used to make the contents for the legends in the graphs,
2226            # this is the name of the model and the elevation.  All in this
2227            # great one liner from DG. If the key 'filename' doesn't exist it
2228            # creates the entry if the entry exist it appends to the key.
2229
2230            legend_list_dic.setdefault(filename,[]) \
2231                .append([directory_name, round(max_ele, 3)])
2232
2233            # creates a LIST for the legend on the last iteration of the
2234            # directories which is when "legend_list_dic" has been fully
2235            # populated. Creates a list of strings which is used in the legend
2236            # only runs on the last iteration for all the gauges(csv) files
2237            # empties the list before creating it
2238
2239            if i == i_max - 1:
2240                legend_list = []
2241   
2242                for name_and_elevation in legend_list_dic[filename]:
2243                    legend_list.append('%s (elevation = %sm)'\
2244                                       % (name_and_elevation[0],
2245                                          name_and_elevation[1]))
2246           
2247            #skip time and elevation so it is not plotted!
2248            for k, quantity in enumerate(quantities):
2249                if quantity != 'time' and quantity != 'elevation':
2250                    pylab.figure(int(k*100+j))
2251                    pylab.ylabel(quantities_label[quantity])
2252                    pylab.plot(t,
2253                               directory_quantity_value[directory]\
2254                                                       [filename][quantity],
2255                               c = cstr[i], linewidth=1)
2256                    pylab.xlabel(quantities_label['time'])
2257                    pylab.axis(quantities_axis[quantity])
2258                    pylab.legend(legend_list,loc='upper right')
2259                   
2260                    pylab.title('%s at %s gauge'
2261                                % (quantity, filename[len(base_name):]))
2262
2263                    if output_dir == '':
2264                        figname = '%s%s%s_%s%s.png' \
2265                                  % (directory, sep, filename, quantity,
2266                                     extra_plot_name)
2267                    else:
2268                        figname = '%s%s%s_%s%s.png' \
2269                                  % (output_dir, sep, filename, quantity,
2270                                     extra_plot_name)
2271
2272                    if verbose: print 'saving figure here %s' %figname
2273
2274                    pylab.savefig(figname)
2275           
2276    if verbose: print 'Closing all plots'
2277
2278    pylab.close('all')
2279    del pylab
2280
2281    if verbose: print 'Finished closing plots'
2282
2283##
2284# @brief Return min and max of an iterable.
2285# @param list The iterable to return min & max of.
2286# @return (min, max) of 'list'.
2287def get_min_max_values(list=None):
2288    """
2289    Returns the min and max of the list it was provided.
2290    """
2291
2292    if list == None: print 'List must be provided'
2293       
2294    return min(list), max(list)
2295
2296
2297##
2298# @brief Get runup around a point in a CSV file.
2299# @param gauge_filename gauge file name.
2300# @param sww_filename SWW file name.
2301# @param runup_filename Name of file to report into.
2302# @param size ??
2303# @param verbose ??
2304def get_runup_data_for_locations_from_file(gauge_filename,
2305                                           sww_filename,
2306                                           runup_filename,
2307                                           size=10,
2308                                           verbose=False):
2309    """this will read a csv file with the header x,y. Then look in a square
2310    'size'x2 around this position for the 'max_inundaiton_height' in the
2311    'sww_filename' and report the findings in the 'runup_filename'.
2312   
2313    WARNING: NO TESTS!
2314    """
2315
2316    from anuga.shallow_water.data_manager import get_all_directories_with_name,\
2317                                                 get_maximum_inundation_data,\
2318                                                 csv2dict
2319                                                 
2320    file = open(runup_filename, "w")
2321    file.write("easting,northing,runup \n ")
2322    file.close()
2323   
2324    #read gauge csv file to dictionary
2325    attribute_dic, title_index_dic = csv2dict(gauge_filename)
2326    northing = [float(x) for x in attribute_dic["y"]]
2327    easting = [float(x) for x in attribute_dic["x"]]
2328
2329    print 'Reading %s' %sww_filename
2330
2331    runup_locations=[]
2332    for i, x in enumerate(northing):
2333        poly = [[int(easting[i]+size),int(northing[i]+size)],
2334                [int(easting[i]+size),int(northing[i]-size)],
2335                [int(easting[i]-size),int(northing[i]-size)],
2336                [int(easting[i]-size),int(northing[i]+size)]]
2337       
2338        run_up, x_y = get_maximum_inundation_data(filename=sww_filename,
2339                                                  polygon=poly,
2340                                                  verbose=False) 
2341
2342        #if no runup will return 0 instead of NONE
2343        if run_up==None: run_up=0
2344        if x_y==None: x_y=[0,0]
2345       
2346        if verbose:
2347            print 'maximum inundation runup near %s is %s meters' %(x_y,run_up)
2348       
2349        #writes to file
2350        file = open(runup_filename, "a")
2351        temp = '%s,%s,%s \n' % (x_y[0], x_y[1], run_up)
2352        file.write(temp)
2353        file.close()
2354
2355
2356##
2357# @brief ??
2358# @param sww_file ??
2359# @param gauge_file ??
2360# @param out_name ??
2361# @param quantities ??
2362# @param verbose ??
2363# @param use_cache ??
2364def sww2csv_gauges(sww_file,
2365                   gauge_file,
2366                   out_name='gauge_',
2367                   quantities=['stage', 'depth', 'elevation',
2368                               'xmomentum', 'ymomentum'],
2369                   verbose=False,
2370                   use_cache = True):
2371    """
2372   
2373    Inputs:
2374       
2375        NOTE: if using csv2timeseries_graphs after creating csv file,
2376        it is essential to export quantities 'depth' and 'elevation'.
2377        'depth' is good to analyse gauges on land and elevation is used
2378        automatically by csv2timeseries_graphs in the legend.
2379       
2380        sww_file: path to any sww file
2381       
2382        gauge_file: Assumes that it follows this format
2383            name, easting, northing, elevation
2384            point1, 100.3, 50.2, 10.0
2385            point2, 10.3, 70.3, 78.0
2386       
2387        NOTE: order of column can change but names eg 'easting', 'elevation'
2388        must be the same! ALL lowercaps!
2389
2390        out_name: prefix for output file name (default is 'gauge_')
2391       
2392    Outputs:
2393        one file for each gauge/point location in the points file. They
2394        will be named with this format in the same directory as the 'sww_file'
2395            <out_name><name>.csv
2396        eg gauge_point1.csv if <out_name> not supplied
2397           myfile_2_point1.csv if <out_name> ='myfile_2_'
2398           
2399           
2400        They will all have a header
2401   
2402    Usage: sww2csv_gauges(sww_file='test1.sww',
2403                          quantities = ['stage', 'elevation','depth','bearing'],
2404                          gauge_file='gauge.txt')   
2405   
2406    Interpolate the quantities at a given set of locations, given
2407    an sww file.
2408    The results are written to a csv file.
2409
2410    In the future let points be a points file.
2411    And the user choose the quantities.
2412
2413    This is currently quite specific.
2414    If it needs to be more general, change things.
2415
2416    This is really returning speed, not velocity.
2417    """
2418   
2419    from csv import reader,writer
2420    from anuga.utilities.numerical_tools import ensure_numeric, mean, NAN
2421    import string
2422    from anuga.shallow_water.data_manager import get_all_swwfiles
2423
2424#    quantities =  ['stage', 'elevation', 'xmomentum', 'ymomentum']
2425    #print "points",points
2426
2427    assert type(gauge_file) == type(''), 'Gauge filename must be a string'
2428    assert type(out_name) == type(''), 'Output filename prefix must be a string'
2429   
2430    try:
2431        point_reader = reader(file(gauge_file))
2432    except Exception, e:
2433        msg = 'File "%s" could not be opened: Error="%s"' % (gauge_file, e)
2434        raise msg
2435
2436    if verbose: print '\n Gauges obtained from: %s \n' %gauge_file
2437   
2438    point_reader = reader(file(gauge_file))
2439    points = []
2440    point_name = []
2441   
2442    #read point info from file
2443    for i,row in enumerate(point_reader):
2444        #read header and determine the column numbers to read correcty.
2445        if i==0:
2446            for j,value in enumerate(row):
2447                if value.strip()=='easting':easting=j
2448                if value.strip()=='northing':northing=j
2449                if value.strip()=='name':name=j
2450                if value.strip()=='elevation':elevation=j
2451        else:
2452            points.append([float(row[easting]),float(row[northing])])
2453            point_name.append(row[name])
2454       
2455    #convert to array for file_function
2456    points_array = num.array(points,num.Float)
2457       
2458    points_array = ensure_absolute(points_array)
2459
2460    dir_name, base = os.path.split(sww_file)   
2461
2462    #need to get current directory so when path and file
2463    #are "joined" below the directory is correct
2464    if dir_name == '':
2465        dir_name =getcwd()
2466       
2467    if access(sww_file,R_OK):
2468        if verbose: print 'File %s exists' %(sww_file)
2469    else:
2470        msg = 'File "%s" could not be opened: no read permission' % sww_file
2471        raise msg
2472
2473    sww_files = get_all_swwfiles(look_in_dir=dir_name,
2474                                 base_name=base,
2475                                 verbose=verbose)
2476   
2477    #to make all the quantities lower case for file_function
2478    quantities = [quantity.lower() for quantity in quantities]
2479
2480    # what is quantities are needed from sww file to calculate output quantities
2481    # also
2482
2483    core_quantities = ['stage', 'elevation', 'xmomentum', 'ymomentum']
2484   
2485    for sww_file in sww_files:
2486        sww_file = join(dir_name, sww_file+'.sww')
2487        callable_sww = file_function(sww_file,
2488                                     quantities=core_quantities,
2489                                     interpolation_points=points_array,
2490                                     verbose=verbose,
2491                                     use_cache=use_cache)
2492    gauge_file = out_name
2493
2494    heading = [quantity for quantity in quantities]
2495    heading.insert(0,'time')
2496
2497    #create a list of csv writers for all the points and write header
2498    points_writer = []
2499    for i,point in enumerate(points):
2500        points_writer.append(writer(file(dir_name + sep + gauge_file
2501                                         + point_name[i] + '.csv', "wb")))
2502        points_writer[i].writerow(heading)
2503   
2504    if verbose: print 'Writing csv files'
2505
2506    for time in callable_sww.get_time():
2507        for point_i, point in enumerate(points_array):
2508            #add domain starttime to relative time.
2509            points_list = [time + callable_sww.starttime]
2510            point_quantities = callable_sww(time,point_i)
2511           
2512            for quantity in quantities:
2513                if quantity == NAN:
2514                    print 'quantity does not exist in' % callable_sww.get_name
2515                else:
2516                    if quantity == 'stage':
2517                        points_list.append(point_quantities[0])
2518                       
2519                    if quantity == 'elevation':
2520                        points_list.append(point_quantities[1])
2521                       
2522                    if quantity == 'xmomentum':
2523                        points_list.append(point_quantities[2])
2524                       
2525                    if quantity == 'ymomentum':
2526                        points_list.append(point_quantities[3])
2527                       
2528                    if quantity == 'depth':
2529                        points_list.append(point_quantities[0] 
2530                                           - point_quantities[1])
2531
2532                    if quantity == 'momentum':
2533                        momentum = sqrt(point_quantities[2]**2 
2534                                        + point_quantities[3]**2)
2535                        points_list.append(momentum)
2536                       
2537                    if quantity == 'speed':
2538                        #if depth is less than 0.001 then speed = 0.0
2539                        if point_quantities[0] - point_quantities[1] < 0.001:
2540                            vel = 0.0
2541                        else:
2542                            if point_quantities[2] < 1.0e6:
2543                                momentum = sqrt(point_quantities[2]**2
2544                                                + point_quantities[3]**2)
2545    #                            vel = momentum/depth             
2546                                vel = momentum / (point_quantities[0] 
2547                                                  - point_quantities[1])
2548    #                            vel = momentum/(depth + 1.e-6/depth)
2549                            else:
2550                                momentum = 0
2551                                vel = 0
2552                           
2553                        points_list.append(vel)
2554                       
2555                    if quantity == 'bearing':
2556                        points_list.append(calc_bearing(point_quantities[2],
2557                                                        point_quantities[3]))
2558
2559            points_writer[point_i].writerow(points_list)
2560       
2561
2562##
2563# @brief Get a wave height at a certain depth given wave height at another depth.
2564# @param d1 The first depth.
2565# @param d2 The second depth.
2566# @param h1 Wave ampitude at d1
2567# @param verbose True if this function is to be verbose.
2568# @return The wave height at d2.
2569def greens_law(d1, d2, h1, verbose=False):
2570    """Green's Law
2571
2572    Green's Law allows an approximation of wave amplitude at
2573    a given depth based on the fourh root of the ratio of two depths
2574    and the amplitude at another given depth.
2575
2576    Note, wave amplitude is equal to stage.
2577   
2578    Inputs:
2579
2580    d1, d2 - the two depths
2581    h1     - the wave amplitude at d1
2582    h2     - the derived amplitude at d2
2583
2584    h2 = h1 (d1/d2)^(1/4), where d2 cannot equal 0.
2585   
2586    """
2587
2588    d1 = ensure_numeric(d1)
2589    d2 = ensure_numeric(d2)
2590    h1 = ensure_numeric(h1)
2591
2592    if d1 <= 0.0:
2593        msg = 'the first depth, d1 (%f), must be strictly positive' % (d1)
2594        raise Exception(msg)
2595
2596    if d2 <= 0.0:
2597        msg = 'the second depth, d2 (%f), must be strictly positive' % (d2)
2598        raise Exception(msg)
2599   
2600    if h1 <= 0.0:
2601        msg = 'the wave amplitude, h1 (%f), must be strictly positive' % (h1)
2602        raise Exception(msg)
2603   
2604    h2 = h1*(d1/d2)**0.25
2605
2606    assert h2 > 0
2607   
2608    return h2
2609       
2610
2611##
2612# @brief Get the square-root of a value.
2613# @param s The value to get the square-root of.
2614# @return The square-root of 's'.
2615def square_root(s):
2616    return sqrt(s)
2617
2618
Note: See TracBrowser for help on using the repository browser.