source: anuga_core/source/anuga/culvert_flows/culvert_class.py @ 6051

Last change on this file since 6051 was 6051, checked in by ole, 16 years ago

Played with culvert class and added update_interval

File size: 17.6 KB
Line 
1from anuga.shallow_water.shallow_water_domain import Inflow, General_forcing
2from anuga.culvert_flows.culvert_polygons import create_culvert_polygons
3from anuga.utilities.system_tools import log_to_file
4from anuga.utilities.polygon import inside_polygon
5from anuga.utilities.polygon import is_inside_polygon
6from anuga.utilities.polygon import plot_polygons
7
8
9
10class Culvert_flow:
11    """Culvert flow - transfer water from one hole to another
12   
13    Using Momentum as Calculated by Culvert Flow !!
14    Could be Several Methods Investigated to do This !!!
15
16    2008_May_08
17    To Ole:
18    OK so here we need to get the Polygon Creating code to create
19    polygons for the culvert Based on
20    the two input Points (X0,Y0) and (X1,Y1) - need to be passed
21    to create polygon
22
23    The two centers are now passed on to create_polygon.
24   
25
26    Input: Two points, pipe_size (either diameter or width, height),
27    mannings_rougness,
28    inlet/outlet energy_loss_coefficients, internal_bend_coefficent,
29    top-down_blockage_factor and bottom_up_blockage_factor
30   
31   
32    And the Delta H enquiry should be change from Openings in line 412
33    to the enquiry Polygons infront of the culvert
34    At the moment this script uses only Depth, later we can change it to
35    Energy...
36
37    Once we have Delta H can calculate a Flow Rate and from Flow Rate
38    an Outlet Velocity
39    The Outlet Velocity x Outlet Depth = Momentum to be applied at the Outlet...
40       
41    Invert levels are optional. If left out they will default to the
42    elevation at the opening.
43       
44    """ 
45
46    def __init__(self,
47                 domain,
48                 label=None, 
49                 description=None,
50                 end_point0=None, 
51                 end_point1=None,
52                 width=None,
53                 height=None,
54                 diameter=None,
55                 manning=None,          # Mannings Roughness for Culvert
56                 invert_level0=None,    # Invert level at opening 0
57                 invert_level1=None,    # Invert level at opening 1
58                 loss_exit=None,
59                 loss_entry=None,
60                 loss_bend=None,
61                 loss_special=None,
62                 blockage_topdwn=None,
63                 blockage_bottup=None,
64                 culvert_routine=None,
65                 number_of_barrels=1,
66                 update_interval=None,
67                 verbose=False):
68       
69        from Numeric import sqrt, sum
70
71        # Input check
72        if diameter is not None:
73            self.culvert_type = 'circle'
74            self.diameter = diameter
75            if height is not None or width is not None:
76                msg = 'Either diameter or width&height must be specified, '
77                msg += 'but not both.'
78                raise Exception, msg
79        else:
80            if height is not None:
81                if width is None:
82                    self.culvert_type = 'square'                               
83                    width = height
84                else:
85                    self.culvert_type = 'rectangle'
86            elif width is not None:
87                if height is None:
88                    self.culvert_type = 'square'                               
89                    height = width
90            else:
91                msg = 'Either diameter or width&height must be specified.'
92                raise Exception, msg               
93               
94            if height == width:
95                self.culvert_type = 'square'                                               
96               
97            self.height = height
98            self.width = width
99
100           
101        assert self.culvert_type in ['circle', 'square', 'rectangle']
102       
103        assert number_of_barrels >= 1
104        self.number_of_barrels = number_of_barrels
105       
106       
107        # Set defaults
108        if manning is None: manning = 0.012   # Default roughness for pipe
109        if loss_exit is None: loss_exit = 1.00
110        if loss_entry is None: loss_entry = 0.50
111        if loss_bend is None: loss_bend=0.00
112        if loss_special is None: loss_special=0.00
113        if blockage_topdwn is None: blockage_topdwn=0.00
114        if blockage_bottup is None: blockage_bottup=0.00
115        if culvert_routine is None: 
116            culvert_routine=boyd_generalised_culvert_model
117           
118        if label is None: label = 'culvert_flow'
119        label += '_' + str(id(self)) 
120        self.label = label
121       
122        # File for storing culvert quantities
123        self.timeseries_filename = label + '_timeseries.csv'
124        fid = open(self.timeseries_filename, 'w')
125        fid.write('time, E0, E1, Velocity, Discharge\n')
126        fid.close()
127
128        # Log file for storing general textual output
129        self.log_filename = label + '.log'         
130        log_to_file(self.log_filename, self.label)       
131        log_to_file(self.log_filename, description)
132        log_to_file(self.log_filename, self.culvert_type)       
133
134
135        # Create the fundamental culvert polygons from POLYGON
136        if self.culvert_type == 'circle':
137            # Redefine width and height for use with create_culvert_polygons
138            width = height = diameter
139       
140        P = create_culvert_polygons(end_point0,
141                                    end_point1,
142                                    width=width,   
143                                    height=height,
144                                    number_of_barrels=number_of_barrels)
145       
146        if verbose is True:
147            pass
148            #plot_polygons([[end_point0, end_point1],
149            #               P['exchange_polygon0'],
150            #               P['exchange_polygon1'],
151            #               P['enquiry_polygon0'],
152            #               P['enquiry_polygon1']],
153            #              figname='culvert_polygon_output')
154            #import sys; sys.exit()                           
155
156
157        # Check that all polygons lie within the mesh.
158        bounding_polygon = domain.get_boundary_polygon()
159        for key in P.keys():
160            if key in ['exchange_polygon0', 
161                       'exchange_polygon1',
162                       'enquiry_polygon0',
163                       'enquiry_polygon1']:
164                for point in P[key]:
165               
166                    msg = 'Point %s in polygon %s for culvert %s did not'\
167                        %(str(point), key, self.label)
168                    msg += 'fall within the domain boundary.'
169                    assert is_inside_polygon(point, bounding_polygon), msg
170       
171
172        # Create inflow object at each end of the culvert.
173        self.openings = []
174        self.openings.append(Inflow(domain,
175                                    polygon=P['exchange_polygon0']))
176
177        self.openings.append(Inflow(domain,
178                                    polygon=P['exchange_polygon1']))                                   
179
180
181        # Assume two openings for now: Referred to as 0 and 1
182        assert len(self.openings) == 2
183       
184        # Store basic geometry
185        self.end_points = [end_point0, end_point1]
186        self.invert_levels = [invert_level0, invert_level1]               
187        #self.enquiry_polygons = [P['enquiry_polygon0'], P['enquiry_polygon1']]
188        self.enquiry_polylines = [P['enquiry_polygon0'][:2], 
189                                  P['enquiry_polygon1'][:2]]
190        self.vector = P['vector']
191        self.length = P['length']; assert self.length > 0.0
192        self.verbose = verbose
193        self.last_time = 0.0       
194        self.last_update = 0.0 # For use with update_interval       
195        self.update_interval = update_interval
196       
197
198        # Store hydraulic parameters
199        self.manning = manning
200        self.loss_exit = loss_exit
201        self.loss_entry = loss_entry
202        self.loss_bend = loss_bend
203        self.loss_special = loss_special
204        self.sum_loss = loss_exit + loss_entry + loss_bend + loss_special
205        self.blockage_topdwn = blockage_topdwn
206        self.blockage_bottup = blockage_bottup
207
208        # Store culvert routine
209        self.culvert_routine = culvert_routine
210
211       
212        # Create objects to update momentum (a bit crude at this stage)
213
214       
215        xmom0 = General_forcing(domain, 'xmomentum',
216                                polygon=P['exchange_polygon0'])
217
218        xmom1 = General_forcing(domain, 'xmomentum',
219                                polygon=P['exchange_polygon1'])
220
221        ymom0 = General_forcing(domain, 'ymomentum',
222                                polygon=P['exchange_polygon0'])
223
224        ymom1 = General_forcing(domain, 'ymomentum',
225                                polygon=P['exchange_polygon1'])
226
227        self.opening_momentum = [ [xmom0, ymom0], [xmom1, ymom1] ]
228       
229
230        # Print something
231        s = 'Culvert Effective Length = %.2f m' %(self.length)
232        log_to_file(self.log_filename, s)
233   
234        s = 'Culvert Direction is %s\n' %str(self.vector)
235        log_to_file(self.log_filename, s)       
236       
237       
238    def __call__(self, domain):
239        from anuga.utilities.numerical_tools import mean
240       
241        from anuga.config import g, epsilon
242        from Numeric import take, sqrt
243        from anuga.config import velocity_protection       
244
245
246        log_filename = self.log_filename
247         
248        # Time stuff
249        time = domain.get_time()
250       
251       
252        update = False
253        if self.update_interval is None:
254            update = True
255        else:   
256            if time - self.last_update > self.update_interval or time == 0.0:
257                update = True
258
259        #print 'call', time, time - self.last_update
260               
261                               
262        if update is True:
263            #print 'Updating', time, time - self.last_update
264            self.last_update = time
265       
266            delta_t = time-self.last_time
267            s = '\nTime = %.2f, delta_t = %f' %(time, delta_t)
268            log_to_file(log_filename, s)
269       
270            msg = 'Time did not advance'
271            if time > 0.0: assert delta_t > 0.0, msg
272
273
274            # Get average water depths at each opening       
275            openings = self.openings   # There are two Opening [0] and [1]
276            for i, opening in enumerate(openings):
277                dq = domain.quantities
278               
279                # Compute mean values of selected quantitites in the
280                # exchange area in front of the culvert
281                # Stage and velocity comes from enquiry area
282                # and elevation from exchange area
283               
284                stage = dq['stage'].get_values(location='centroids',
285                                               indices=opening.exchange_indices)           
286                w = mean(stage) # Average stage
287
288                # Use invert level instead of elevation if specified
289                invert_level = self.invert_levels[i]
290                if invert_level is not None:
291                    z = invert_level
292                else:
293                    elevation = dq['elevation'].get_values(location='centroids', 
294                                                           indices=opening.exchange_indices)
295                    z = mean(elevation) # Average elevation
296
297                # Estimated depth above the culvert inlet
298                d = w - z  # Used for calculations involving depth
299                if d < 0.0:
300                    # This is possible since w and z are taken at different locations
301                    #msg = 'D < 0.0: %f' %d
302                    #raise msg
303                    d = 0.0
304               
305
306                # Ratio of depth to culvert height.
307                # If ratio > 1 then culvert is running full
308                if self.culvert_type == 'circle':
309                    ratio = d/self.diameter
310                else:   
311                    ratio = d/self.height 
312                opening.ratio = ratio
313                   
314                   
315                # Average measures of energy in front of this opening
316                polyline = self.enquiry_polylines[i]
317                #print 't = %.4f, opening=%d,' %(domain.time, i),
318                opening.total_energy = domain.get_energy_through_cross_section(polyline,
319                                                                               kind='total')           
320                #print 'Et = %.3f m' %opening.total_energy
321
322                # Store current average stage and depth with each opening object
323                opening.depth = d
324                opening.depth_trigger = d # Use this for now
325                opening.stage = w
326                opening.elevation = z
327               
328
329            #################  End of the FOR loop ################################################
330
331            # We now need to deal with each opening individually
332               
333            # Determine flow direction based on total energy difference
334            delta_Et = openings[0].total_energy - openings[1].total_energy
335
336            if delta_Et > 0:
337                #print 'Flow U/S ---> D/S'
338                inlet = openings[0]
339                outlet = openings[1]
340
341                inlet.momentum = self.opening_momentum[0]
342                outlet.momentum = self.opening_momentum[1]
343
344            else:
345                #print 'Flow D/S ---> U/S'
346                inlet = openings[1]
347                outlet = openings[0]
348
349                inlet.momentum = self.opening_momentum[1]
350                outlet.momentum = self.opening_momentum[0]
351               
352                delta_Et = -delta_Et
353
354            self.inlet = inlet
355            self.outlet = outlet
356               
357            msg = 'Total energy difference is negative'
358            assert delta_Et > 0.0, msg
359
360            delta_h = inlet.stage - outlet.stage
361            delta_z = inlet.elevation - outlet.elevation
362            culvert_slope = (delta_z/self.length)
363
364            if culvert_slope < 0.0:
365                # Adverse gradient - flow is running uphill
366                # Flow will be purely controlled by uphill outlet face
367                if self.verbose is True:
368                    print 'WARNING: Flow is running uphill. Watch Out!', inlet.elevation, outlet.elevation
369
370
371            s = 'Delta total energy = %.3f' %(delta_Et)
372            log_to_file(log_filename, s)
373
374
375            # Calculate discharge for one barrel and set inlet.rate and outlet.rate
376            Q, barrel_velocity, culvert_outlet_depth = self.culvert_routine(self, inlet, outlet, delta_Et, g)
377       
378            # Adjust discharge for multiple barrels
379            Q *= self.number_of_barrels
380
381            # Compute barrel momentum
382            barrel_momentum = barrel_velocity*culvert_outlet_depth
383                   
384            s = 'Barrel velocity = %f' %barrel_velocity
385            log_to_file(log_filename, s)
386
387            # Compute momentum vector at outlet
388            outlet_mom_x, outlet_mom_y = self.vector * barrel_momentum
389               
390            s = 'Directional momentum = (%f, %f)' %(outlet_mom_x, outlet_mom_y)
391            log_to_file(log_filename, s)
392
393            # Log timeseries to file
394            fid = open(self.timeseries_filename, 'a')       
395            fid.write('%f, %f, %f, %f, %f\n'\
396                          %(time, 
397                            openings[0].total_energy,
398                            openings[1].total_energy,
399                            barrel_velocity,
400                            Q))
401            fid.close()
402
403            # Update momentum       
404            delta_t = time - self.last_time
405            if delta_t > 0.0:
406                xmomentum_rate = outlet_mom_x - outlet.momentum[0].value
407                xmomentum_rate /= delta_t
408                   
409                ymomentum_rate = outlet_mom_y - outlet.momentum[1].value
410                ymomentum_rate /= delta_t
411                       
412                s = 'X Y MOM_RATE = (%f, %f) ' %(xmomentum_rate, ymomentum_rate)
413                log_to_file(log_filename, s)                   
414            else:
415                xmomentum_rate = ymomentum_rate = 0.0
416
417
418            # Set momentum rates for outlet jet
419            outlet.momentum[0].rate = xmomentum_rate
420            outlet.momentum[1].rate = ymomentum_rate
421
422            # Remember this value for next step (IMPORTANT)
423            outlet.momentum[0].value = outlet_mom_x
424            outlet.momentum[1].value = outlet_mom_y                   
425
426            if int(domain.time*100) % 100 == 0:
427                s = 'T=%.5f, Culvert Discharge = %.3f f'\
428                    %(time, Q)
429                s += ' Depth= %0.3f  Momentum = (%0.3f, %0.3f)'\
430                     %(culvert_outlet_depth, outlet_mom_x,outlet_mom_y)
431                s += ' Momentum rate: (%.4f, %.4f)'\
432                     %(xmomentum_rate, ymomentum_rate)                   
433                s+='Outlet Vel= %.3f'\
434                    %(barrel_velocity)
435                log_to_file(log_filename, s)
436           
437               
438
439
440        # Execute flow term for each opening
441        # This is where Inflow objects are evaluated and update the domain
442        for opening in self.openings:
443            opening(domain)
444
445        # Execute momentum terms
446        # This is where Inflow objects are evaluated and update the domain
447        self.outlet.momentum[0](domain)
448        self.outlet.momentum[1](domain)       
449           
450        # Store value of time #FIXME(Ole): Maybe only every time we update   
451        self.last_time = time
452
453
Note: See TracBrowser for help on using the repository browser.