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

Last change on this file since 3984 was 3984, checked in by duncan, 17 years ago

comments

File size: 43.5 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, basename
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=0, numprocs=1):
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    if plot_quantity is None:
696        plot_quantity = ['depth', 'speed', 'bearing']
697    else:
698        assert type(plot_quantity) == list,\
699               'plot_quantity must be a list'
700        check_list(plot_quantity)
701
702    if surface is None:
703        surface = False
704       
705    if title_on is None:
706        title_on = True
707   
708    if verbose: print '\n Gauges obtained from: %s \n' %gauge_filename
709    gauges, locations, elev = get_gauges_from_file(gauge_filename)
710
711    sww_quantity = ['stage', 'elevation', 'xmomentum', 'ymomentum']
712
713    file_loc = []
714    f_list = []
715    label_id = []
716    leg_label = []
717    themaxT = 0.0
718    theminT = 0.0
719   
720    for swwfile in swwfiles.keys():
721
722        try:
723            fid = open(swwfile)
724        except Exception, e:
725            msg = 'File "%s" could not be opened: Error="%s"'\
726                  %(swwfile, e)
727            raise msg
728
729        f = file_function(swwfile,
730                          quantities = sww_quantity,
731                          interpolation_points = gauges,
732                          verbose = True,
733                          use_cache = True)
734
735        # determine which gauges are contained in sww file
736        count = 0
737        gauge_index = []
738        print 'swwfile', swwfile
739        for k, g in enumerate(gauges):
740            if f(0.0, point_id = k)[2] > 1.0e6:
741                count += 1
742                if count == 1: print 'Gauges not contained here:'
743                print locations[k]
744            else:
745                gauge_index.append(k)
746
747        if len(gauge_index) > 0:
748            print 'Gauges contained here: \n',
749        else:
750            print 'No gauges contained here. \n'
751        for i in range(len(gauge_index)):
752             print locations[gauge_index[i]]
753             
754        index = swwfile.rfind(sep)
755        file_loc.append(swwfile[:index+1])
756        label_id.append(swwfiles[swwfile])
757        leg_label.append(production_dirs[swwfiles[swwfile]])
758       
759        f_list.append(f)
760        maxT = max(f.get_time())
761        minT = min(f.get_time())
762        if maxT > themaxT: themaxT = maxT
763        if minT > theminT: theminT = minT
764
765    if time_min is None:
766        time_min = theminT # min(T)
767    else:
768        if time_min < theminT: # min(T):
769            msg = 'Minimum time entered not correct - please try again'
770            raise Exception, msg
771
772    if time_max is None:
773        time_max = themaxT # max(T)
774    else:
775        if time_max > themaxT: # max(T):
776            msg = 'Maximum time entered not correct - please try again'
777            raise Exception, msg
778
779    if verbose and len(gauge_index) > 0: print 'Inputs OK - going to generate figures'
780
781    if len(gauge_index) <> 0:
782        texfile, elev_output = generate_figures(plot_quantity, file_loc, report, reportname, surface,
783                                                leg_label, f_list, gauges, locations, elev, gauge_index,
784                                                production_dirs, time_min, time_max, title_on, label_id, verbose)
785    else:
786        texfile = ''
787        elev_output = []
788
789    return texfile, elev_output
790                         
791#Fixme - Use geospatial to read this file - it's an xya file
792#Need to include other information into this filename, so xya + Name - required for report
793def get_gauges_from_file(filename):
794    """ Read in gauge information from file
795    """
796    from os import sep, getcwd, access, F_OK, mkdir
797    fid = open(filename)
798    lines = fid.readlines()
799    fid.close()
800   
801    gauges = []
802    gaugelocation = []
803    elev = []
804    line1 = lines[0]
805    line11 = line1.split(',')
806    east_index = len(line11)+1
807    north_index = len(line11)+1
808    name_index = len(line11)+1
809    elev_index = len(line11)+1
810    for i in range(len(line11)):
811        if line11[i].strip('\n').strip(' ').lower() == 'easting': east_index = i
812        if line11[i].strip('\n').strip(' ').lower() == 'northing': north_index = i
813        if line11[i].strip('\n').strip(' ').lower() == 'name': name_index = i
814        if line11[i].strip('\n').strip(' ').lower() == 'elevation': elev_index = i
815
816    for line in lines[1:]:
817        fields = line.split(',')
818        if east_index < len(line11) and north_index < len(line11):
819            gauges.append([float(fields[east_index]), float(fields[north_index])])
820        else:
821            msg = 'WARNING: %s does not contain location information' %(filename)
822            raise Exception, msg
823        if elev_index < len(line11): elev.append(float(fields[elev_index]))
824        if name_index < len(line11):
825            loc = fields[name_index]
826            gaugelocation.append(loc.strip('\n'))
827
828    return gauges, gaugelocation, elev
829
830def check_list(quantity):
831    """ Check that input quantities in quantity list are possible
832    """
833    all_quantity = ['stage', 'depth', 'momentum', 'xmomentum',
834                    'ymomentum', 'speed', 'bearing', 'elevation']
835
836    for i,j in enumerate(quantity):
837        quantity[i] = quantity[i].lower()
838    p = list(set(quantity).difference(set(all_quantity)))
839    if len(p) <> 0:
840        msg = 'Quantities %s do not exist - please try again' %p
841        raise Exception, msg
842       
843    return 
844
845def calc_bearing(uh, vh):
846    """ Calculate velocity bearing from North
847    """
848    from math import atan, degrees
849   
850    angle = degrees(atan(vh/(uh+1.e-15)))
851    if (0 < angle < 90.0):
852        if vh > 0:
853            bearing = 90.0 - abs(angle)
854        if vh < 0:
855            bearing = 270.0 - abs(angle)
856    if (-90 < angle < 0):
857        if vh < 0:
858            bearing = 90.0 - abs(angle)
859        if vh > 0:
860            bearing = 270.0 - abs(angle)
861    if angle == 0: bearing = 0.0
862           
863    return bearing
864
865def generate_figures(plot_quantity, file_loc, report, reportname, surface,
866                     leg_label, f_list, gauges, locations, elev, gauge_index,
867                     production_dirs, time_min, time_max, title_on, label_id,
868                     verbose):
869    """ Generate figures based on required quantities and gauges for each sww file
870    """
871    from math import sqrt, atan, degrees
872    from Numeric import ones, allclose, zeros, Float, ravel
873    from os import sep, altsep, getcwd, mkdir, access, F_OK, environ
874    from pylab import ion, hold, plot, axis, figure, legend, savefig, \
875         xlabel, ylabel, title, close, subplot
876
877    import pylab as p1
878    if surface is True:
879        import mpl3d.mplot3d as p3
880       
881    if report == True:   
882        texdir = getcwd()+sep+'report'+sep
883        if access(texdir,F_OK) == 0:
884            mkdir (texdir)
885        if len(label_id) == 1:
886            label_id1 = label_id[0].replace(sep,'')
887            label_id2 = label_id1.replace('_','')
888            texfile = texdir+reportname+'%s' %(label_id2)
889            texfile2 = reportname+'%s' %(label_id2)
890            texfilename = texfile + '.tex'
891            if verbose: print '\n Latex output printed to %s \n' %texfilename
892            fid = open(texfilename, 'w')
893        else:
894            texfile = texdir+reportname
895            texfile2 = reportname
896            texfilename = texfile + '.tex' 
897            if verbose: print '\n Latex output printed to %s \n' %texfilename
898            fid = open(texfilename, 'w')
899    else:
900        texfile = ''
901        texfile2 = ''
902
903    p = len(f_list)
904    n = []
905    n0 = 0
906    for i in range(len(f_list)):
907        n.append(len(f_list[i].get_time()))
908        if n[i] > n0: n0 = n[i] 
909    n0 = int(n0)
910    m = len(locations)
911    model_time = zeros((n0,m,p), Float) 
912    stages = zeros((n0,m,p), Float)
913    elevations = zeros((n0,m,p), Float) 
914    momenta = zeros((n0,m,p), Float)
915    xmom = zeros((n0,m,p), Float)
916    ymom = zeros((n0,m,p), Float)
917    speed = zeros((n0,m,p), Float)
918    bearings = zeros((n0,m,p), Float)
919    depths = zeros((n0,m,p), Float)
920    eastings = zeros((n0,m,p), Float)
921    min_stages = []
922    max_stages = []
923    max_momentums = []
924    max_speeds = []
925    c = 0
926    model_time_plot3d = zeros((n0,m), Float)
927    stages_plot3d = zeros((n0,m), Float)
928    eastings_plot3d = zeros((n0,m),Float)
929    ##### loop over each swwfile #####
930    for j, f in enumerate(f_list):
931        if verbose: print 'swwfile %d of %d' %(j, len(f_list))
932        comparefile = file_loc[j]+sep+'gauges_maxmins'+'.csv'
933        fid_compare = open(comparefile, 'w')
934        ##### loop over each gauge #####
935        for k in gauge_index:
936            g = gauges[k]
937            if verbose: print 'Gauge %d of %d' %(k, len(gauges))
938            min_stage = 10
939            max_stage = 0
940            max_momentum = 0
941            max_speed = 0   
942            gaugeloc = locations[k]
943            thisfile = file_loc[j]+sep+'gauges_time_series'+'_'+gaugeloc+'.csv'
944            fid_out = open(thisfile, 'w')
945            s = 'Time, Stage, Momentum, Speed \n'
946            fid_out.write(s)
947           
948            #### generate quantities #######
949            for i, t in enumerate(f.get_time()):
950                if time_min <= t <= time_max:
951                    w = f(t, point_id = k)[0]
952                    z = f(t, point_id = k)[1]
953                    uh = f(t, point_id = k)[2]
954                    vh = f(t, point_id = k)[3]
955                    depth = w-z     
956                    m = sqrt(uh*uh + vh*vh)   
957                    if depth < 0.001:
958                        vel = 0.0
959                    else:
960                        vel = m / (depth + 1.e-30) 
961                    bearing = calc_bearing(uh, vh)
962                    model_time[i,k,j] = t/60.0
963                    stages[i,k,j] = w
964                    elevations[i,k,j] = z
965                    xmom[i,k,j] = uh
966                    ymom[i,k,j] = vh
967                    momenta[i,k,j] = m
968                    speed[i,k,j] = vel
969                    bearings[i,k,j] = bearing
970                    depths[i,k,j] = depth
971                    thisgauge = gauges[k]
972                    eastings[i,k,j] = thisgauge[0]
973                    s = '%.2f, %.2f, %.2f, %.2f\n' %(t, w, m, vel)
974                    fid_out.write(s)
975                    if t/60.0 <= 13920: tindex = i
976                    if w > max_stage: max_stage = w
977                    if w < min_stage: min_stage = w
978                    if m > max_momentum: max_momentum = m
979                    if vel > max_speed: max_speed = vel
980                   
981                   
982            s = '%.2f, %.2f, %.2f, %s\n' %(max_stage, min_stage, z, leg_label[j])
983            fid_compare.write(s)
984            max_stages.append(max_stage)
985            min_stages.append(min_stage)
986            max_momentums.append(max_momentum)
987            max_speeds.append(max_speed)     
988            #### finished generating quantities for each swwfile #####
989       
990        model_time_plot3d[:,:] = model_time[:,:,j]
991        stages_plot3d[:,:] = stages[:,:,j]
992        eastings_plot3d[:,] = eastings[:,:,j]
993           
994        if surface ==  True:
995            print 'Printing surface figure'
996            for i in range(2):
997                fig = p1.figure(10)
998                ax = p3.Axes3D(fig)
999                if len(gauges) > 80:
1000                    ax.plot_surface(model_time[:,:,j],eastings[:,:,j],stages[:,:,j])
1001                else:
1002                    #ax.plot_wireframe(model_time[:,:,j],eastings[:,:,j],stages[:,:,j])
1003                    ax.plot3D(ravel(eastings[:,:,j]),ravel(model_time[:,:,j]),ravel(stages[:,:,j]))
1004                ax.set_xlabel('time')
1005                ax.set_ylabel('x')
1006                ax.set_zlabel('stage')
1007                fig.add_axes(ax)
1008                p1.show()
1009                surfacefig = 'solution_surface%s' %leg_label[j]
1010                p1.savefig(surfacefig)
1011                p1.close()
1012           
1013    #### finished generating quantities for all swwfiles #####
1014
1015    # x profile for given time
1016    if surface == True:
1017        figure(11)
1018        plot(eastings[tindex,:,j],stages[tindex,:,j])
1019        xlabel('x')
1020        ylabel('stage')
1021        profilefig = 'solution_xprofile' 
1022        savefig('profilefig')
1023               
1024    stage_axis = axis([time_min/60.0, time_max/60.0, min(min_stages), max(max_stages)*1.1])
1025    #stage_axis = axis([time_min/60.0, time_max/60.0, -3.0, 3.0])   
1026    vel_axis = axis([time_min/60.0, time_max/60.0, min(max_speeds), max(max_speeds)*1.1])
1027    mom_axis = axis([time_min/60.0, time_max/60.0, min(max_momentums), max(max_momentums)*1.1]) 
1028   
1029    cstr = ['g', 'r', 'b', 'c', 'm', 'y', 'k']
1030    nn = len(plot_quantity)
1031    no_cols = 2
1032    elev_output = []
1033    if len(label_id) > 1: graphname_report = []
1034    for k in gauge_index:
1035        g = gauges[k]
1036        count1 = 0
1037        if report == True and len(label_id) > 1:
1038            s = '\\begin{figure}[hbt] \n \\centering \n \\begin{tabular}{cc} \n'
1039            fid.write(s)
1040        if len(label_id) > 1: graphname_report = []
1041        #### generate figures for each gauge ####
1042        for j, f in enumerate(f_list):
1043            ion()
1044            hold(True)
1045            count = 0
1046            where1 = 0
1047            where2 = 0
1048            word_quantity = ''
1049            if report == True and len(label_id) == 1:
1050                s = '\\begin{figure}[hbt] \n \\centering \n \\begin{tabular}{cc} \n'
1051                fid.write(s)
1052               
1053            for which_quantity in plot_quantity:
1054                count += 1
1055                where1 += 1
1056                figure(count, frameon = False)
1057                if which_quantity == 'depth':
1058                    plot(model_time[0:n[j]-1,k,j], depths[0:n[j]-1,k,j], '-', c = cstr[j])
1059                    units = 'm'
1060                if which_quantity == 'stage':
1061                    plot(model_time[0:n[j]-1,k,j], stages[0:n[j]-1,k,j], '-', c = cstr[j])
1062                    axis(stage_axis)
1063                    units = 'm'
1064                if which_quantity == 'momentum':
1065                    plot(model_time[0:n[j]-1,k,j], momenta[0:n[j]-1,k,j], '-', c = cstr[j])
1066                    axis(mom_axis)
1067                    units = 'm^2 / sec'
1068                if which_quantity == 'xmomentum':
1069                    plot(model_time[0:n[j]-1,k,j], xmom[0:n[j]-1,k,j], '-', c = cstr[j])
1070                    axis(mom_axis)
1071                    units = 'm^2 / sec'
1072                if which_quantity == 'ymomentum':
1073                    plot(model_time[0:n[j]-1,k,j], ymom[0:n[j]-1,k,j], '-', c = cstr[j])
1074                    axis(mom_axis)
1075                    units = 'm^2 / sec'
1076                if which_quantity == 'speed':
1077                    plot(model_time[0:n[j]-1,k,j], speed[0:n[j]-1,k,j], '-', c = cstr[j])
1078                    axis(vel_axis)
1079                    units = 'm / sec'
1080                if which_quantity == 'bearing':
1081                    due_east = 90.0*ones(shape(model_time[0:n[j]-1,k,j],Float))
1082                    due_west = 270.0*ones(shape(model_time[0:n[j]-1,k,j],Float))
1083                    plot(model_time[0:n[j]-1,k,j], bearings, '-', 
1084                         model_time[0:n[j]-1,k,j], due_west, '-.', 
1085                         model_time[0:n[j]-1,k,j], due_east, '-.')
1086                    units = 'degrees from North'
1087                    ax = axis([time_min, time_max, 0.0, 360.0])
1088                    legend(('Bearing','West','East'))
1089
1090                xlabel('time (mins)')
1091                ylabel('%s (%s)' %(which_quantity, units))
1092                if len(label_id) > 1: legend((leg_label),loc='upper right')
1093
1094                gaugeloc1 = gaugeloc.replace(' ','')
1095                #gaugeloc2 = gaugeloc1.replace('_','')
1096                gaugeloc2 = locations[k].replace(' ','')
1097                graphname = '%sgauge%s_%s' %(file_loc[j], gaugeloc2, which_quantity)
1098
1099                if report == True and len(label_id) > 1:
1100                    figdir = getcwd()+sep+'report_figures'+sep
1101                    if access(figdir,F_OK) == 0 :
1102                        mkdir (figdir)
1103                    latex_file_loc = figdir.replace(sep,altsep) 
1104                    graphname_latex = '%sgauge%s%s' %(latex_file_loc, gaugeloc2, which_quantity) # storing files in production directory   
1105                    graphname_report_input = '%sgauge%s%s' %('..'+altsep+'report_figures'+altsep, gaugeloc2, which_quantity) # giving location in latex output file
1106                    graphname_report.append(graphname_report_input)
1107                   
1108                    savefig(graphname_latex) # save figures in production directory for report generation
1109
1110                if report == True:
1111
1112                    figdir = getcwd()+sep+'report_figures'+sep
1113                    if access(figdir,F_OK) == 0 :
1114                        mkdir (figdir)
1115                    latex_file_loc = figdir.replace(sep,altsep)   
1116
1117                    if len(label_id) == 1: 
1118                        graphname_latex = '%sgauge%s%s%s' %(latex_file_loc, gaugeloc2, which_quantity, label_id2) # storing files in production directory 
1119                        graphname_report = '%sgauge%s%s%s' %('..'+altsep+'report_figures'+altsep, gaugeloc2, which_quantity, label_id2) # giving location in latex output file
1120                        s = '\includegraphics[width=0.49\linewidth, height=50mm]{%s%s}' %(graphname_report, '.png')
1121                        fid.write(s)
1122                        if where1 % 2 == 0:
1123                            s = '\\\\ \n'
1124                            where1 = 0
1125                        else:
1126                            s = '& \n'
1127                        fid.write(s)
1128                        savefig(graphname_latex)
1129               
1130                if title_on == True:
1131                    title('%s scenario: %s at %s gauge' %(label_id, which_quantity, gaugeloc2))
1132
1133                savefig(graphname) # save figures with sww file
1134
1135            if report == True and len(label_id) == 1:
1136                for i in range(nn-1):
1137                    if nn > 2:
1138                        word_quantity += plot_quantity[i] + ', '
1139                    else:
1140                        word_quantity += plot_quantity[i]
1141                   
1142                word_quantity += ' and ' + plot_quantity[nn-1]
1143                caption = 'Time series for %s at %s location (elevation %.2fm)' %(word_quantity, locations[k], elev[k]) #gaugeloc.replace('_',' '))
1144                if elev[k] == 0.0:
1145                    caption = 'Time series for %s at %s location (elevation %.2fm)' %(word_quantity, locations[k], elevations[0,k,j])
1146                    east = gauges[0]
1147                    north = gauges[1]
1148                    elev_output.append([locations[k],east,north,elevations[0,k,j]])
1149                label = '%sgauge%s' %(label_id2, gaugeloc2)
1150                s = '\end{tabular} \n \\caption{%s} \n \label{fig:%s} \n \end{figure} \n \n' %(caption, label)
1151                fid.write(s)
1152                c += 1
1153                if c % 6 == 0: fid.write('\\clearpage \n')
1154                savefig(graphname_latex)               
1155               
1156        if report == True and len(label_id) > 1:
1157            for i in range(nn-1):
1158                if nn > 2:
1159                    word_quantity += plot_quantity[i] + ', '
1160                else:
1161                    word_quantity += plot_quantity[i]
1162                where1 = 0
1163                count1 += 1
1164                index = j*len(plot_quantity)
1165                for which_quantity in plot_quantity:
1166                    where1 += 1
1167                    s = '\includegraphics[width=0.49\linewidth, height=50mm]{%s%s}' %(graphname_report[index], '.png')
1168                    index += 1
1169                    fid.write(s)
1170                    if where1 % 2 == 0:
1171                        s = '\\\\ \n'
1172                        where1 = 0
1173                    else:
1174                        s = '& \n'
1175                    fid.write(s)
1176            word_quantity += ' and ' + plot_quantity[nn-1]           
1177            label = 'gauge%s' %(gaugeloc2) 
1178            caption = 'Time series for %s at %s location (elevation %.2fm)' %(word_quantity, locations[k], elev[k])
1179            if elev[k] == 0.0:
1180                    caption = 'Time series for %s at %s location (elevation %.2fm)' %(word_quantity, locations[k], elevations[0,k,j])
1181                    thisgauge = gauges[k]
1182                    east = thisgauge[0]
1183                    north = thisgauge[1]
1184                    elev_output.append([locations[k],east,north,elevations[0,k,j]])
1185           
1186            s = '\end{tabular} \n \\caption{%s} \n \label{fig:%s} \n \end{figure} \n \n' %(caption, label)
1187            fid.write(s)
1188            c += 1
1189            if c % 6 == 0: fid.write('\\clearpage \n')         
1190           
1191            #### finished generating figures ###
1192
1193        close('all')
1194   
1195    return texfile2, elev_output
1196
1197# FIXME (DSG): Add unit test, make general, not just 2 files,
1198# but any number of files.
1199def copy_code_files(dir_name, filename1, filename2):
1200    """Copies "filename1" and "filename2" to "dir_name". Very useful for
1201    information management """
1202
1203    if access(dir_name,F_OK) == 0:
1204        print 'Make directory %s' %dir_name
1205        mkdir (dir_name,0777)
1206    copy(filename1, dir_name + sep + basename(filename1))
1207    copy(filename2, dir_name + sep + basename(filename2))
1208#    copy (__file__, project.output_run_time_dir + basename(__file__))
1209    print 'Files %s and %s copied' %(filename1, filename2)
1210
1211def add_directories(root_directory, directories):
1212    """
1213    Add the first directory in directories to root_directory.
1214    Then add the second
1215    direcotory to the first directory and so on.
1216
1217    Return the path of the final directory.
1218
1219    This is handy for specifying and creating a directory where data will go.
1220    """
1221    dir = root_directory
1222    for new_dir in directories:
1223        dir = dir + sep + new_dir
1224        if not access(dir,F_OK):
1225            mkdir (dir)
1226    return dir
1227
1228
1229
Note: See TracBrowser for help on using the repository browser.