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

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

Replaced 'print' statements with log.critical() calls.

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