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

Last change on this file since 3935 was 3935, checked in by nick, 17 years ago

update code style

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