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

Last change on this file since 4009 was 4009, checked in by sexton, 17 years ago

updates

File size: 43.6 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, sep
12from os.path import exists, basename
13from warnings import warn
14from shutil import copy
15
16from anuga.geospatial_data.geospatial_data import ensure_absolute
17
18def file_function(filename,
19                  domain=None,
20                  quantities=None,
21                  interpolation_points=None,
22                  time_thinning=1,
23                  verbose=False,
24                  use_cache=False):
25    """Read time history of spatial data from NetCDF file and return
26    a callable object.
27
28    Input variables:
29   
30    filename - Name of sww or tms file
31       
32       If the file has extension 'sww' then it is assumed to be spatio-temporal
33       or temporal and the callable object will have the form f(t,x,y) or f(t)
34       depending on whether the file contains spatial data
35
36       If the file has extension 'tms' then it is assumed to be temporal only
37       and the callable object will have the form f(t)
38
39       Either form will return interpolated values based on the input file
40       using the underlying interpolation_function.
41
42    domain - Associated domain object   
43       If domain is specified, model time (domain.starttime)
44       will be checked and possibly modified.
45   
46       All times are assumed to be in UTC
47       
48       All spatial information is assumed to be in absolute UTM coordinates.
49
50    quantities - the name of the quantity to be interpolated or a
51                 list of quantity names. The resulting function will return
52                 a tuple of values - one for each quantity 
53
54    interpolation_points - list of absolute UTM coordinates for points (N x 2)
55    or geospatial object or points file name at which values are sought
56   
57    use_cache: True means that caching of intermediate result of
58               Interpolation_function is attempted
59
60   
61    See Interpolation function for further documentation
62    """
63
64
65    # Build arguments and keyword arguments for use with caching or apply.
66    args = (filename,)
67   
68    kwargs = {'domain': domain,
69              'quantities': quantities,
70              'interpolation_points': interpolation_points,
71              'time_thinning': time_thinning,                   
72              'verbose': verbose}
73
74
75    # Call underlying engine with or without caching
76    if use_cache is True:
77        try:
78            from caching import cache
79        except:
80            msg = 'Caching was requested, but caching module'+\
81                  'could not be imported'
82            raise msg
83
84        f = cache(_file_function,
85                  args, kwargs,
86                  dependencies=[filename],
87                  compression=False,                 
88                  verbose=verbose)
89
90    else:
91        f = apply(_file_function,
92                  args, kwargs)
93
94
95    #FIXME (Ole): Pass cache arguments, such as compression, in some sort of
96    #structure
97       
98
99    return f
100
101
102
103def _file_function(filename,
104                   domain=None,
105                   quantities=None,
106                   interpolation_points=None,
107                   time_thinning=1,                                               
108                   verbose=False):
109    """Internal function
110   
111    See file_function for documentatiton
112    """
113   
114
115    #FIXME (OLE): Should check origin of domain against that of file
116    #In fact, this is where origin should be converted to that of domain
117    #Also, check that file covers domain fully.
118
119    #Take into account:
120    #- domain's georef
121    #- sww file's georef
122    #- interpolation points as absolute UTM coordinates
123
124
125    assert type(filename) == type(''),\
126               'First argument to File_function must be a string'
127
128    try:
129        fid = open(filename)
130    except Exception, e:
131        msg = 'File "%s" could not be opened: Error="%s"'\
132                  %(filename, e)
133        raise msg
134
135    line = fid.readline()
136    fid.close()
137
138    if quantities is None: 
139        if domain is not None:
140            quantities = domain.conserved_quantities
141
142
143
144    if line[:3] == 'CDF':
145        return get_netcdf_file_function(filename, domain, quantities,
146                                        interpolation_points,
147                                        time_thinning=time_thinning,
148                                        verbose=verbose)
149    else:
150        raise 'Must be a NetCDF File'
151
152
153
154def get_netcdf_file_function(filename,
155                             domain=None,
156                             quantity_names=None,
157                             interpolation_points=None,
158                             time_thinning=1,                             
159                             verbose=False):
160    """Read time history of spatial data from NetCDF sww file and
161    return a callable object f(t,x,y)
162    which will return interpolated values based on the input file.
163
164    If domain is specified, model time (domain.starttime)
165    will be checked and possibly modified
166   
167    All times are assumed to be in UTC
168
169    See Interpolation function for further documetation
170   
171    """
172   
173   
174    #FIXME: Check that model origin is the same as file's origin
175    #(both in UTM coordinates)
176    #If not - modify those from file to match domain
177    #Take this code from e.g. dem2pts in data_manager.py
178    #FIXME: Use geo_reference to read and write xllcorner...
179       
180
181    #FIXME: Maybe move caching out to this level rather than at the
182    #Interpolation_function level (below)
183
184    import time, calendar, types
185    from anuga.config import time_format
186    from Scientific.IO.NetCDF import NetCDFFile
187    from Numeric import array, zeros, Float, alltrue, concatenate, reshape
188
189    #Open NetCDF file
190    if verbose: print 'Reading', filename
191    fid = NetCDFFile(filename, 'r')
192
193    if type(quantity_names) == types.StringType:
194        quantity_names = [quantity_names]       
195   
196    if quantity_names is None or len(quantity_names) < 1:
197        #If no quantities are specified get quantities from file
198        #x, y, time are assumed as the independent variables so
199        #they are excluded as potentiol quantities
200        quantity_names = []
201        for name in fid.variables.keys():
202            if name not in ['x', 'y', 'time']:
203                quantity_names.append(name)
204
205    if len(quantity_names) < 1:               
206        msg = 'ERROR: At least one independent value must be specified'
207        raise msg
208
209
210    if interpolation_points is not None:
211        interpolation_points = ensure_absolute(interpolation_points)
212        msg = 'Points must by N x 2. I got %d' %interpolation_points.shape[1]
213        assert interpolation_points.shape[1] == 2, msg
214
215
216    #Now assert that requested quantitites (and the independent ones)
217    #are present in file
218    missing = []
219    for quantity in ['time'] + quantity_names:
220        if not fid.variables.has_key(quantity):
221            missing.append(quantity)
222
223    if len(missing) > 0:
224        msg = 'Quantities %s could not be found in file %s'\
225              %(str(missing), filename)
226        fid.close()
227        raise Exception, msg
228
229    #Decide whether this data has a spatial dimension
230    spatial = True
231    for quantity in ['x', 'y']:
232        if not fid.variables.has_key(quantity):
233            spatial = False
234
235    if filename[-3:] == 'tms' and spatial is True:
236        msg = 'Files of type tms must not contain spatial information'
237        raise msg
238
239    if filename[-3:] == 'sww' and spatial is False:
240        msg = 'Files of type sww must contain spatial information'       
241        raise msg       
242
243    #Get first timestep
244    try:
245        starttime = fid.starttime[0]
246    except ValueError:
247        msg = 'Could not read starttime from file %s' %filename
248        raise msg
249
250    #Get variables
251    #if verbose: print 'Get variables'   
252    time = fid.variables['time'][:]   
253
254    # Get time independent stuff
255    if spatial:
256        #Get origin
257        xllcorner = fid.xllcorner[0]
258        yllcorner = fid.yllcorner[0]
259        zone = fid.zone[0]       
260
261        x = fid.variables['x'][:]
262        y = fid.variables['y'][:]
263        triangles = fid.variables['volumes'][:]
264
265        x = reshape(x, (len(x),1))
266        y = reshape(y, (len(y),1))
267        vertex_coordinates = concatenate((x,y), axis=1) #m x 2 array
268
269        if interpolation_points is not None:
270            #Adjust for georef
271            interpolation_points[:,0] -= xllcorner
272            interpolation_points[:,1] -= yllcorner       
273       
274
275
276
277    if domain is not None:
278        #Update domain.startime if it is *earlier* than starttime
279        if starttime > domain.starttime:
280            msg = 'WARNING: Start time as specified in domain (%f)'\
281                  %domain.starttime
282            msg += ' is earlier than the starttime of file %s (%f).'\
283                     %(filename, starttime)
284            msg += ' Modifying domain starttime accordingly.'
285           
286            if verbose: print msg
287            domain.starttime = starttime #Modifying model time
288            if verbose: print 'Domain starttime is now set to %f'\
289               %domain.starttime
290
291
292        #If domain.startime is *later* than starttime,
293        #move time back - relative to domain's time
294        if domain.starttime > starttime:
295            time = time - domain.starttime + starttime
296
297        #FIXME Use method in geo to reconcile
298        #if spatial:
299        #assert domain.geo_reference.xllcorner == xllcorner
300        #assert domain.geo_reference.yllcorner == yllcorner
301        #assert domain.geo_reference.zone == zone       
302       
303    if verbose:
304        print 'File_function data obtained from: %s' %filename
305        print '  References:'
306        #print '    Datum ....' #FIXME
307        if spatial:
308            print '    Lower left corner: [%f, %f]'\
309                  %(xllcorner, yllcorner)
310        print '    Start time:   %f' %starttime               
311       
312   
313    # Produce values for desired data points at
314    # each timestep for each quantity
315    quantities = {}
316    for i, name in enumerate(quantity_names):
317        quantities[name] = fid.variables[name][:]
318    fid.close()
319   
320
321    #from least_squares import Interpolation_function
322    from anuga.fit_interpolate.interpolate import Interpolation_function
323
324    if not spatial:
325        vertex_coordinates = triangles = interpolation_points = None         
326
327
328    return Interpolation_function(time,
329                                  quantities,
330                                  quantity_names,
331                                  vertex_coordinates,
332                                  triangles,
333                                  interpolation_points,
334                                  time_thinning=time_thinning,
335                                  verbose=verbose)
336
337
338
339
340
341def multiple_replace(text, dictionary):
342    """Multiple replace of words in text
343
344    text:       String to be modified
345    dictionary: Mapping of words that are to be substituted
346
347    Python Cookbook 3.14 page 88 and page 90
348    """
349
350    import re
351   
352    #Create a regular expression from all of the dictionary keys
353    #matching only entire words
354    regex = re.compile(r'\b'+ \
355                       r'\b|\b'.join(map(re.escape, dictionary.keys()))+ \
356                       r'\b' )
357
358    #For each match, lookup the corresponding value in the dictionary
359    return regex.sub(lambda match: dictionary[match.group(0)], text)
360
361
362
363
364def apply_expression_to_dictionary(expression, dictionary):#dictionary):
365    """Apply arbitrary expression to values of dictionary
366
367    Given an expression in terms of the keys, replace key by the
368    corresponding values and evaluate.
369   
370
371    expression: Arbitrary, e.g. arithmetric, expression relating keys
372                from dictionary.
373
374    dictionary: Mapping between symbols in expression and objects that
375                will be evaluated by expression.
376                Values in dictionary must support operators given in
377                expression e.g. by overloading
378
379    due to a limitation with Numeric, this can not evaluate 0/0
380    In general, the user can fix by adding 1e-30 to the numerator.
381    SciPy core can handle this situation.
382    """
383
384    import types
385    import re
386
387    assert isinstance(expression, basestring)
388    assert type(dictionary) == types.DictType
389
390    #Convert dictionary values to textual representations suitable for eval
391    D = {}
392    for key in dictionary:
393        D[key] = 'dictionary["%s"]' %key
394
395    #Perform substitution of variables   
396    expression = multiple_replace(expression, D)
397
398    #Evaluate and return
399    try:
400        return eval(expression)
401    except NameError, e:
402        msg = 'Expression "%s" could not be evaluated: %s' %(expression, e)
403        raise NameError, msg
404    except ValueError, e:
405        msg = 'Expression "%s" could not be evaluated: %s' %(expression, e)
406        raise ValueError, msg
407   
408
409
410
411####################################
412####OBSOLETE STUFF
413
414
415def angle(v1, v2):
416    """Temporary Interface to new location"""
417
418    import anuga.utilities.numerical_tools as NT       
419   
420    msg = 'angle has moved from util.py.  '
421    msg += 'Please use "from anuga.utilities.numerical_tools import angle"'
422    warn(msg, DeprecationWarning) 
423
424    return NT.angle(v1, v2)
425   
426def anglediff(v0, v1):
427    """Temporary Interface to new location"""
428
429    import anuga.utilities.numerical_tools as NT
430   
431    msg = 'anglediff has moved from util.py.  '
432    msg += 'Please use "from anuga.utilities.numerical_tools import anglediff"'
433    warn(msg, DeprecationWarning) 
434
435    return NT.anglediff(v0, v1)   
436
437   
438def mean(x):
439    """Temporary Interface to new location"""
440
441    import anuga.utilities.numerical_tools as NT   
442   
443    msg = 'mean has moved from util.py.  '
444    msg += 'Please use "from anuga.utilities.numerical_tools import mean"'
445    warn(msg, DeprecationWarning) 
446
447    return NT.mean(x)   
448
449def point_on_line(*args, **kwargs):
450    """Temporary Interface to new location"""
451
452    msg = 'point_on_line has moved from util.py.  '
453    msg += 'Please use "from anuga.utilities.polygon import point_on_line"'
454    warn(msg, DeprecationWarning) 
455
456    return utilities.polygon.point_on_line(*args, **kwargs)     
457   
458def inside_polygon(*args, **kwargs):
459    """Temporary Interface to new location"""
460
461    print 'inside_polygon has moved from util.py.  ',
462    print 'Please use "from anuga.utilities.polygon import inside_polygon"'
463
464    return utilities.polygon.inside_polygon(*args, **kwargs)   
465   
466def outside_polygon(*args, **kwargs):
467    """Temporary Interface to new location"""
468
469    print 'outside_polygon has moved from util.py.  ',
470    print 'Please use "from anuga.utilities.polygon import outside_polygon"'
471
472    return utilities.polygon.outside_polygon(*args, **kwargs)   
473
474
475def separate_points_by_polygon(*args, **kwargs):
476    """Temporary Interface to new location"""
477
478    print 'separate_points_by_polygon has moved from util.py.  ',
479    print 'Please use "from anuga.utilities.polygon import separate_points_by_polygon"'
480
481    return utilities.polygon.separate_points_by_polygon(*args, **kwargs)   
482
483
484
485def read_polygon(*args, **kwargs):
486    """Temporary Interface to new location"""
487
488    print 'read_polygon has moved from util.py.  ',
489    print 'Please use "from anuga.utilities.polygon import read_polygon"'
490
491    return utilities.polygon.read_polygon(*args, **kwargs)   
492
493
494def populate_polygon(*args, **kwargs):
495    """Temporary Interface to new location"""
496
497    print 'populate_polygon has moved from util.py.  ',
498    print 'Please use "from anuga.utilities.polygon import populate_polygon"'
499
500    return utilities.polygon.populate_polygon(*args, **kwargs)   
501
502##################### end of obsolete stuff ? ############
503
504def start_screen_catcher(dirname, myid=0, numprocs=1):
505    """Used to store screen output and errors to file, if run on multiple
506    processes eachprocessor will have its own output and error file.
507    """
508
509    dirname = dirname
510    screen_output_name = dirname + "screen_output_%d_%d.txt" %(myid,numprocs)
511    screen_error_name = dirname + "screen_error_%d_%d.txt" %(myid,numprocs)
512
513    #used to catch screen output to file
514    sys.stdout = Screen_Catcher(screen_output_name)
515    sys.stderr = Screen_Catcher(screen_error_name)
516
517class Screen_Catcher:
518    """this simply catches the screen output and stores it to file defined by
519    start_screen_catcher (above)
520    """
521   
522    def __init__(self, filename):
523        self.filename = filename
524        if exists(self.filename)is True:
525            remove(self.filename)
526            print'Old existing file "%s" has been deleted' %(self.filename)
527
528    def write(self, stuff):
529        fid = open(self.filename, 'a')
530        fid.write(stuff)
531
532def get_version_info():
533    """gets the version number of the SVN
534    """
535   
536    import os, sys
537
538    # Create dummy info
539    info = 'Revision: Version info could not be obtained.'
540    info += 'A command line version of svn and access to the '
541    info += 'repository is necessary and the output must '
542    info += 'contain a line starting with "Revision:"'
543
544    try:
545        fid = os.popen('svn info')
546    except:
547        msg = 'svn is not recognised'
548        warn(msg, UserWarning)
549    else:   
550        lines = fid.readlines()
551        fid.close()
552        for line in lines:
553            if line.startswith('Revision:'):
554                info = line
555                break
556       
557    return info
558   
559    #if sys.platform == 'win32':
560    #    msg = 'does not work in Windows .... yet'
561    #    raise OSError, msg
562    #else:   
563    #    fid = os.popen('svn -info')
564    #    info = fid.readlines()
565    #    fid.close()
566    #    return info
567   
568   
569def sww2timeseries(swwfiles,
570                   gauge_filename,
571                   production_dirs,
572                   report = None,
573                   reportname = None,
574                   plot_quantity = None,
575                   surface = None,
576                   time_min = None,
577                   time_max = None,
578                   title_on = None,
579                   verbose = False):
580   
581    """ Read sww file and plot the time series for the
582    prescribed quantities at defined gauge locations and
583    prescribed time range.
584
585    Input variables:
586
587    swwfiles        - dictionary of sww files with label_ids (used in
588                      generating latex output. It will be part of
589                      the directory name of file_loc (typically the timestamp).
590                      Helps to differentiate latex files for different simulations
591                      for a particular scenario. 
592                    - assume that all conserved quantities have been stored
593                    - assume each sww file has been simulated with same timestep
594   
595    gauge_filename  - name of file containing gauge data
596                        - easting, northing, name , elevation?
597                    - OR (this is not yet done)
598                        - structure which can be converted to a Numeric array,
599                          such as a geospatial data object
600                     
601    report          - if True, then write figures to report_figures directory in
602                      relevant production directory
603                    - if False, figures are already stored with sww file
604                    - default to False
605
606    reportname      - name for report if wishing to generate report
607   
608    plot_quantity   - list containing quantities to plot, they must
609                      be the name of an existing quantity or one of
610                      the following possibilities
611                    - possibilities:
612                        - stage; 'stage'
613                        - depth; 'depth'
614                        - speed; calculated as absolute momentum
615                         (pointwise) divided by depth; 'speed'
616                        - bearing; calculated as the angle of the momentum
617                          vector (xmomentum, ymomentum) from the North; 'bearing'
618                        - absolute momentum; calculated as
619                          sqrt(xmomentum^2 + ymomentum^2); 'momentum'
620                        - x momentum; 'xmomentum'
621                        - y momentum; 'ymomentum'
622                    - default will be ['stage', 'speed', 'bearing']
623
624    surface          - if True, then generate solution surface with 3d plot
625                       and save to current working directory
626                     - default = False
627   
628    time_min         - beginning of user defined time range for plotting purposes
629                        - default will be first available time found in swwfile
630                       
631    time_max         - end of user defined time range for plotting purposes
632                        - default will be last available time found in swwfile
633                       
634    title_on        - if True, export standard graphics with title
635                    - if False, export standard graphics without title
636
637
638                     
639    Output:
640   
641    - time series data stored in .csv for later use if required.
642      Name = gauges_timeseries followed by gauge name
643    - latex file will be generated in same directory as where script is
644      run (usually production scenario directory.
645      Name = latexoutputlabel_id.tex
646
647    Other important information:
648   
649    It is assumed that the used has stored all the conserved quantities
650    and elevation during the scenario run, i.e.
651    ['stage', 'elevation', 'xmomentum', 'ymomentum']
652    If this has not occurred then sww2timeseries will not work.
653                     
654    """
655
656   
657    k = _sww2timeseries(swwfiles,
658                        gauge_filename,
659                        production_dirs,
660                        report,
661                        reportname,
662                        plot_quantity,
663                        surface,
664                        time_min,
665                        time_max,
666                        title_on,
667                        verbose)
668
669    return k
670
671def _sww2timeseries(swwfiles,
672                    gauge_filename,
673                    production_dirs,
674                    report = None,
675                    reportname = None,
676                    plot_quantity = None,
677                    surface = None,
678                    time_min = None,
679                    time_max = None,
680                    title_on = None,
681                    verbose = False):   
682       
683    assert type(gauge_filename) == type(''),\
684           'Gauge filename must be a string'
685   
686    try:
687        fid = open(gauge_filename)
688    except Exception, e:
689        msg = 'File "%s" could not be opened: Error="%s"'\
690                  %(gauge_filename, e)
691        raise msg
692
693    if report is None:
694        report = False
695       
696    if plot_quantity is None:
697        plot_quantity = ['depth', 'speed', 'bearing']
698    else:
699        assert type(plot_quantity) == list,\
700               'plot_quantity must be a list'
701        check_list(plot_quantity)
702
703    if surface is None:
704        surface = False
705       
706    if title_on is None:
707        title_on = True
708   
709    if verbose: print '\n Gauges obtained from: %s \n' %gauge_filename
710    gauges, locations, elev = get_gauges_from_file(gauge_filename)
711
712    sww_quantity = ['stage', 'elevation', 'xmomentum', 'ymomentum']
713
714    file_loc = []
715    f_list = []
716    label_id = []
717    leg_label = []
718    themaxT = 0.0
719    theminT = 0.0
720   
721    for swwfile in swwfiles.keys():
722
723        try:
724            fid = open(swwfile)
725        except Exception, e:
726            msg = 'File "%s" could not be opened: Error="%s"'\
727                  %(swwfile, e)
728            raise msg
729
730        f = file_function(swwfile,
731                          quantities = sww_quantity,
732                          interpolation_points = gauges,
733                          verbose = True,
734                          use_cache = True)
735
736        # determine which gauges are contained in sww file
737        count = 0
738        gauge_index = []
739        print 'swwfile', swwfile
740        for k, g in enumerate(gauges):
741            if f(0.0, point_id = k)[2] > 1.0e6:
742                count += 1
743                if count == 1: print 'Gauges not contained here:'
744                print locations[k]
745            else:
746                gauge_index.append(k)
747
748        if len(gauge_index) > 0:
749            print 'Gauges contained here: \n',
750        else:
751            print 'No gauges contained here. \n'
752        for i in range(len(gauge_index)):
753             print locations[gauge_index[i]]
754             
755        index = swwfile.rfind(sep)
756        file_loc.append(swwfile[:index+1])
757        label_id.append(swwfiles[swwfile])
758        leg_label.append(production_dirs[swwfiles[swwfile]])
759       
760        f_list.append(f)
761        maxT = max(f.get_time())
762        minT = min(f.get_time())
763        if maxT > themaxT: themaxT = maxT
764        if minT > theminT: theminT = minT
765
766    if time_min is None:
767        time_min = theminT # min(T)
768    else:
769        if time_min < theminT: # min(T):
770            msg = 'Minimum time entered not correct - please try again'
771            raise Exception, msg
772
773    if time_max is None:
774        time_max = themaxT # max(T)
775    else:
776        if time_max > themaxT: # max(T):
777            msg = 'Maximum time entered not correct - please try again'
778            raise Exception, msg
779
780    if verbose and len(gauge_index) > 0: print 'Inputs OK - going to generate figures'
781
782    if len(gauge_index) <> 0:
783        texfile, elev_output = generate_figures(plot_quantity, file_loc, report, reportname, surface,
784                                                leg_label, f_list, gauges, locations, elev, gauge_index,
785                                                production_dirs, time_min, time_max, title_on, label_id, verbose)
786    else:
787        texfile = ''
788        elev_output = []
789
790    return texfile, elev_output
791                         
792#Fixme - Use geospatial to read this file - it's an xya file
793#Need to include other information into this filename, so xya + Name - required for report
794def get_gauges_from_file(filename):
795    """ Read in gauge information from file
796    """
797    from os import sep, getcwd, access, F_OK, mkdir
798    fid = open(filename)
799    lines = fid.readlines()
800    fid.close()
801   
802    gauges = []
803    gaugelocation = []
804    elev = []
805    line1 = lines[0]
806    line11 = line1.split(',')
807    east_index = len(line11)+1
808    north_index = len(line11)+1
809    name_index = len(line11)+1
810    elev_index = len(line11)+1
811    for i in range(len(line11)):
812        if line11[i].strip('\n').strip(' ').lower() == 'easting': east_index = i
813        if line11[i].strip('\n').strip(' ').lower() == 'northing': north_index = i
814        if line11[i].strip('\n').strip(' ').lower() == 'name': name_index = i
815        if line11[i].strip('\n').strip(' ').lower() == 'elevation': elev_index = i
816
817    for line in lines[1:]:
818        fields = line.split(',')
819        if east_index < len(line11) and north_index < len(line11):
820            gauges.append([float(fields[east_index]), float(fields[north_index])])
821        else:
822            msg = 'WARNING: %s does not contain location information' %(filename)
823            raise Exception, msg
824        if elev_index < len(line11): elev.append(float(fields[elev_index]))
825        if name_index < len(line11):
826            loc = fields[name_index]
827            gaugelocation.append(loc.strip('\n'))
828
829    return gauges, gaugelocation, elev
830
831def check_list(quantity):
832    """ Check that input quantities in quantity list are possible
833    """
834    all_quantity = ['stage', 'depth', 'momentum', 'xmomentum',
835                    'ymomentum', 'speed', 'bearing', 'elevation']
836
837    for i,j in enumerate(quantity):
838        quantity[i] = quantity[i].lower()
839    p = list(set(quantity).difference(set(all_quantity)))
840    if len(p) <> 0:
841        msg = 'Quantities %s do not exist - please try again' %p
842        raise Exception, msg
843       
844    return 
845
846def calc_bearing(uh, vh):
847    """ Calculate velocity bearing from North
848    """
849    from math import atan, degrees
850   
851    angle = degrees(atan(vh/(uh+1.e-15)))
852    if (0 < angle < 90.0):
853        if vh > 0:
854            bearing = 90.0 - abs(angle)
855        if vh < 0:
856            bearing = 270.0 - abs(angle)
857    if (-90 < angle < 0):
858        if vh < 0:
859            bearing = 90.0 - abs(angle)
860        if vh > 0:
861            bearing = 270.0 - abs(angle)
862    if angle == 0: bearing = 0.0
863           
864    return bearing
865
866def generate_figures(plot_quantity, file_loc, report, reportname, surface,
867                     leg_label, f_list, gauges, locations, elev, gauge_index,
868                     production_dirs, time_min, time_max, title_on, label_id,
869                     verbose):
870    """ Generate figures based on required quantities and gauges for each sww file
871    """
872    from math import sqrt, atan, degrees
873    from Numeric import ones, allclose, zeros, Float, ravel
874    from os import sep, altsep, getcwd, mkdir, access, F_OK, environ
875    from pylab import ion, hold, plot, axis, figure, legend, savefig, \
876         xlabel, ylabel, title, close, subplot
877
878    import pylab as p1
879    if surface is True:
880        import mpl3d.mplot3d as p3
881       
882    if report == True:   
883        texdir = getcwd()+sep+'report'+sep
884        if access(texdir,F_OK) == 0:
885            mkdir (texdir)
886        if len(label_id) == 1:
887            label_id1 = label_id[0].replace(sep,'')
888            label_id2 = label_id1.replace('_','')
889            texfile = texdir+reportname+'%s' %(label_id2)
890            texfile2 = reportname+'%s' %(label_id2)
891            texfilename = texfile + '.tex'
892            if verbose: print '\n Latex output printed to %s \n' %texfilename
893            fid = open(texfilename, 'w')
894        else:
895            texfile = texdir+reportname
896            texfile2 = reportname
897            texfilename = texfile + '.tex' 
898            if verbose: print '\n Latex output printed to %s \n' %texfilename
899            fid = open(texfilename, 'w')
900    else:
901        texfile = ''
902        texfile2 = ''
903
904    p = len(f_list)
905    n = []
906    n0 = 0
907    for i in range(len(f_list)):
908        n.append(len(f_list[i].get_time()))
909        if n[i] > n0: n0 = n[i] 
910    n0 = int(n0)
911    m = len(locations)
912    model_time = zeros((n0,m,p), Float) 
913    stages = zeros((n0,m,p), Float)
914    elevations = zeros((n0,m,p), Float) 
915    momenta = zeros((n0,m,p), Float)
916    xmom = zeros((n0,m,p), Float)
917    ymom = zeros((n0,m,p), Float)
918    speed = zeros((n0,m,p), Float)
919    bearings = zeros((n0,m,p), Float)
920    depths = zeros((n0,m,p), Float)
921    eastings = zeros((n0,m,p), Float)
922    min_stages = []
923    max_stages = []
924    max_momentums = []
925    max_speeds = []
926    c = 0
927    model_time_plot3d = zeros((n0,m), Float)
928    stages_plot3d = zeros((n0,m), Float)
929    eastings_plot3d = zeros((n0,m),Float)
930    ##### loop over each swwfile #####
931    for j, f in enumerate(f_list):
932        if verbose: print 'swwfile %d of %d' %(j, len(f_list))
933        comparefile = file_loc[j]+sep+'gauges_maxmins'+'.csv'
934        fid_compare = open(comparefile, 'w')
935        ##### loop over each gauge #####
936        for k in gauge_index:
937            g = gauges[k]
938            if verbose: print 'Gauge %d of %d' %(k, len(gauges))
939            min_stage = 10
940            max_stage = 0
941            max_momentum = 0
942            max_speed = 0   
943            gaugeloc = locations[k]
944            thisfile = file_loc[j]+sep+'gauges_time_series'+'_'+gaugeloc+'.csv'
945            fid_out = open(thisfile, 'w')
946            s = 'Time, Stage, Momentum, Speed, Elevation \n'
947            fid_out.write(s)
948           
949            #### generate quantities #######
950            for i, t in enumerate(f.get_time()):
951                if time_min <= t <= time_max:
952                    w = f(t, point_id = k)[0]
953                    z = f(t, point_id = k)[1]
954                    uh = f(t, point_id = k)[2]
955                    vh = f(t, point_id = k)[3]
956                    depth = w-z     
957                    m = sqrt(uh*uh + vh*vh)   
958                    if depth < 0.001:
959                        vel = 0.0
960                    else:
961                        vel = m / (depth + 1.e-30) 
962                    bearing = calc_bearing(uh, vh)
963                    model_time[i,k,j] = t/60.0
964                    stages[i,k,j] = w
965                    elevations[i,k,j] = z
966                    xmom[i,k,j] = uh
967                    ymom[i,k,j] = vh
968                    momenta[i,k,j] = m
969                    speed[i,k,j] = vel
970                    bearings[i,k,j] = bearing
971                    depths[i,k,j] = depth
972                    thisgauge = gauges[k]
973                    eastings[i,k,j] = thisgauge[0]
974                    s = '%.2f, %.2f, %.2f, %.2f, %.2f\n' %(t, w, m, vel, z)
975                    fid_out.write(s)
976                    if t/60.0 <= 13920: tindex = i
977                    if w > max_stage: max_stage = w
978                    if w < min_stage: min_stage = w
979                    if m > max_momentum: max_momentum = m
980                    if vel > max_speed: max_speed = vel
981                   
982                   
983            s = '%.2f, %.2f, %.2f, %.2f, %s\n' %(max_stage, min_stage, z, thisgauge[0], leg_label[j])
984            fid_compare.write(s)
985            max_stages.append(max_stage)
986            min_stages.append(min_stage)
987            max_momentums.append(max_momentum)
988            max_speeds.append(max_speed)     
989            #### finished generating quantities for each swwfile #####
990       
991        model_time_plot3d[:,:] = model_time[:,:,j]
992        stages_plot3d[:,:] = stages[:,:,j]
993        eastings_plot3d[:,] = eastings[:,:,j]
994           
995        if surface ==  True:
996            print 'Printing surface figure'
997            for i in range(2):
998                fig = p1.figure(10)
999                ax = p3.Axes3D(fig)
1000                if len(gauges) > 80:
1001                    ax.plot_surface(model_time[:,:,j],eastings[:,:,j],stages[:,:,j])
1002                else:
1003                    #ax.plot_wireframe(model_time[:,:,j],eastings[:,:,j],stages[:,:,j])
1004                    ax.plot3D(ravel(eastings[:,:,j]),ravel(model_time[:,:,j]),ravel(stages[:,:,j]))
1005                ax.set_xlabel('time')
1006                ax.set_ylabel('x')
1007                ax.set_zlabel('stage')
1008                fig.add_axes(ax)
1009                p1.show()
1010                surfacefig = 'solution_surface%s' %leg_label[j]
1011                p1.savefig(surfacefig)
1012                p1.close()
1013           
1014    #### finished generating quantities for all swwfiles #####
1015
1016    # x profile for given time
1017    if surface == True:
1018        figure(11)
1019        plot(eastings[tindex,:,j],stages[tindex,:,j])
1020        xlabel('x')
1021        ylabel('stage')
1022        profilefig = 'solution_xprofile' 
1023        savefig('profilefig')
1024               
1025    #stage_axis = axis([time_min/60.0, time_max/60.0, min(min_stages), max(max_stages)*1.1])
1026    #stage_axis = axis([time_min/60.0, time_max/60.0, -3.0, 3.0])   
1027    #vel_axis = axis([time_min/60.0, time_max/60.0, min(max_speeds), max(max_speeds)*1.1])
1028    #mom_axis = axis([time_min/60.0, time_max/60.0, min(max_momentums), max(max_momentums)*1.1]) 
1029   
1030    cstr = ['g', 'r', 'b', 'c', 'm', 'y', 'k']
1031    nn = len(plot_quantity)
1032    no_cols = 2
1033    elev_output = []
1034    if len(label_id) > 1: graphname_report = []
1035    for k in gauge_index:
1036        g = gauges[k]
1037        count1 = 0
1038        if report == True and len(label_id) > 1:
1039            s = '\\begin{figure}[hbt] \n \\centering \n \\begin{tabular}{cc} \n'
1040            fid.write(s)
1041        if len(label_id) > 1: graphname_report = []
1042        #### generate figures for each gauge ####
1043        for j, f in enumerate(f_list):
1044            ion()
1045            hold(True)
1046            count = 0
1047            where1 = 0
1048            where2 = 0
1049            word_quantity = ''
1050            if report == True and len(label_id) == 1:
1051                s = '\\begin{figure}[hbt] \n \\centering \n \\begin{tabular}{cc} \n'
1052                fid.write(s)
1053               
1054            for which_quantity in plot_quantity:
1055                count += 1
1056                where1 += 1
1057                figure(count, frameon = False)
1058                if which_quantity == 'depth':
1059                    plot(model_time[0:n[j]-1,k,j], depths[0:n[j]-1,k,j], '-', c = cstr[j])
1060                    units = 'm'
1061                if which_quantity == 'stage':
1062                    plot(model_time[0:n[j]-1,k,j], stages[0:n[j]-1,k,j], '-', c = cstr[j])
1063                    #axis(stage_axis)
1064                    units = 'm'
1065                if which_quantity == 'momentum':
1066                    plot(model_time[0:n[j]-1,k,j], momenta[0:n[j]-1,k,j], '-', c = cstr[j])
1067                    #axis(mom_axis)
1068                    units = 'm^2 / sec'
1069                if which_quantity == 'xmomentum':
1070                    plot(model_time[0:n[j]-1,k,j], xmom[0:n[j]-1,k,j], '-', c = cstr[j])
1071                    #axis(mom_axis)
1072                    units = 'm^2 / sec'
1073                if which_quantity == 'ymomentum':
1074                    plot(model_time[0:n[j]-1,k,j], ymom[0:n[j]-1,k,j], '-', c = cstr[j])
1075                    #axis(mom_axis)
1076                    units = 'm^2 / sec'
1077                if which_quantity == 'speed':
1078                    plot(model_time[0:n[j]-1,k,j], speed[0:n[j]-1,k,j], '-', c = cstr[j])
1079                    #axis(vel_axis)
1080                    units = 'm / sec'
1081                if which_quantity == 'bearing':
1082                    due_east = 90.0*ones(shape(model_time[0:n[j]-1,k,j],Float))
1083                    due_west = 270.0*ones(shape(model_time[0:n[j]-1,k,j],Float))
1084                    plot(model_time[0:n[j]-1,k,j], bearings, '-', 
1085                         model_time[0:n[j]-1,k,j], due_west, '-.', 
1086                         model_time[0:n[j]-1,k,j], due_east, '-.')
1087                    units = 'degrees from North'
1088                    ax = axis([time_min, time_max, 0.0, 360.0])
1089                    legend(('Bearing','West','East'))
1090
1091                xlabel('time (mins)')
1092                ylabel('%s (%s)' %(which_quantity, units))
1093                if len(label_id) > 1: legend((leg_label),loc='upper right')
1094
1095                gaugeloc1 = gaugeloc.replace(' ','')
1096                #gaugeloc2 = gaugeloc1.replace('_','')
1097                gaugeloc2 = locations[k].replace(' ','')
1098                graphname = '%sgauge%s_%s' %(file_loc[j], gaugeloc2, which_quantity)
1099
1100                if report == True and len(label_id) > 1:
1101                    figdir = getcwd()+sep+'report_figures'+sep
1102                    if access(figdir,F_OK) == 0 :
1103                        mkdir (figdir)
1104                    latex_file_loc = figdir.replace(sep,altsep) 
1105                    graphname_latex = '%sgauge%s%s' %(latex_file_loc, gaugeloc2, which_quantity) # storing files in production directory   
1106                    graphname_report_input = '%sgauge%s%s' %('..'+altsep+'report_figures'+altsep, gaugeloc2, which_quantity) # giving location in latex output file
1107                    graphname_report.append(graphname_report_input)
1108                   
1109                    savefig(graphname_latex) # save figures in production directory for report generation
1110
1111                if report == True:
1112
1113                    figdir = getcwd()+sep+'report_figures'+sep
1114                    if access(figdir,F_OK) == 0 :
1115                        mkdir (figdir)
1116                    latex_file_loc = figdir.replace(sep,altsep)   
1117
1118                    if len(label_id) == 1: 
1119                        graphname_latex = '%sgauge%s%s%s' %(latex_file_loc, gaugeloc2, which_quantity, label_id2) # storing files in production directory 
1120                        graphname_report = '%sgauge%s%s%s' %('..'+altsep+'report_figures'+altsep, gaugeloc2, which_quantity, label_id2) # giving location in latex output file
1121                        s = '\includegraphics[width=0.49\linewidth, height=50mm]{%s%s}' %(graphname_report, '.png')
1122                        fid.write(s)
1123                        if where1 % 2 == 0:
1124                            s = '\\\\ \n'
1125                            where1 = 0
1126                        else:
1127                            s = '& \n'
1128                        fid.write(s)
1129                        savefig(graphname_latex)
1130               
1131                if title_on == True:
1132                    title('%s scenario: %s at %s gauge' %(label_id, which_quantity, gaugeloc2))
1133
1134                savefig(graphname) # save figures with sww file
1135
1136            if report == True and len(label_id) == 1:
1137                for i in range(nn-1):
1138                    if nn > 2:
1139                        word_quantity += plot_quantity[i] + ', '
1140                    else:
1141                        word_quantity += plot_quantity[i]
1142                   
1143                word_quantity += ' and ' + plot_quantity[nn-1]
1144                caption = 'Time series for %s at %s location (elevation %.2fm)' %(word_quantity, locations[k], elev[k]) #gaugeloc.replace('_',' '))
1145                if elev[k] == 0.0:
1146                    caption = 'Time series for %s at %s location (elevation %.2fm)' %(word_quantity, locations[k], elevations[0,k,j])
1147                    east = gauges[0]
1148                    north = gauges[1]
1149                    elev_output.append([locations[k],east,north,elevations[0,k,j]])
1150                label = '%sgauge%s' %(label_id2, gaugeloc2)
1151                s = '\end{tabular} \n \\caption{%s} \n \label{fig:%s} \n \end{figure} \n \n' %(caption, label)
1152                fid.write(s)
1153                c += 1
1154                if c % 6 == 0: fid.write('\\clearpage \n')
1155                savefig(graphname_latex)               
1156               
1157        if report == True and len(label_id) > 1:
1158            for i in range(nn-1):
1159                if nn > 2:
1160                    word_quantity += plot_quantity[i] + ', '
1161                else:
1162                    word_quantity += plot_quantity[i]
1163                where1 = 0
1164                count1 += 1
1165                index = j*len(plot_quantity)
1166                for which_quantity in plot_quantity:
1167                    where1 += 1
1168                    s = '\includegraphics[width=0.49\linewidth, height=50mm]{%s%s}' %(graphname_report[index], '.png')
1169                    index += 1
1170                    fid.write(s)
1171                    if where1 % 2 == 0:
1172                        s = '\\\\ \n'
1173                        where1 = 0
1174                    else:
1175                        s = '& \n'
1176                    fid.write(s)
1177            word_quantity += ' and ' + plot_quantity[nn-1]           
1178            label = 'gauge%s' %(gaugeloc2) 
1179            caption = 'Time series for %s at %s location (elevation %.2fm)' %(word_quantity, locations[k], elev[k])
1180            if elev[k] == 0.0:
1181                    caption = 'Time series for %s at %s location (elevation %.2fm)' %(word_quantity, locations[k], elevations[0,k,j])
1182                    thisgauge = gauges[k]
1183                    east = thisgauge[0]
1184                    north = thisgauge[1]
1185                    elev_output.append([locations[k],east,north,elevations[0,k,j]])
1186                   
1187            s = '\end{tabular} \n \\caption{%s} \n \label{fig:%s} \n \end{figure} \n \n' %(caption, label)
1188            fid.write(s)
1189            c += 1
1190            if c % 6 == 0: fid.write('\\clearpage \n')         
1191           
1192            #### finished generating figures ###
1193
1194        close('all')
1195   
1196    return texfile2, elev_output
1197
1198# FIXME (DSG): Add unit test, make general, not just 2 files,
1199# but any number of files.
1200def copy_code_files(dir_name, filename1, filename2):
1201    """Copies "filename1" and "filename2" to "dir_name". Very useful for
1202    information management """
1203
1204    if access(dir_name,F_OK) == 0:
1205        print 'Make directory %s' %dir_name
1206        mkdir (dir_name,0777)
1207    copy(filename1, dir_name + sep + basename(filename1))
1208    copy(filename2, dir_name + sep + basename(filename2))
1209#    copy (__file__, project.output_run_time_dir + basename(__file__))
1210    print 'Files %s and %s copied' %(filename1, filename2)
1211
1212
1213def add_directories(root_directory, directories):
1214    """
1215    Add the first directory in directories to root_directory.
1216    Then add the second
1217    direcotory to the first directory and so on.
1218
1219    Return the path of the final directory.
1220
1221    This is handy for specifying and creating a directory where data will go.
1222    """
1223    dir = root_directory
1224    for new_dir in directories:
1225        dir = os.path.join(dir, new_dir)
1226        if not access(dir,F_OK):
1227            mkdir (dir)
1228    return dir
1229
1230
1231
Note: See TracBrowser for help on using the repository browser.