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

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

Worked on energy line enquiry for culverts - this is rather slow at the moment

File size: 16.1 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                 verbose=False):
67       
68        from Numeric import sqrt, sum
69
70        # Input check
71        if diameter is not None:
72            self.culvert_type = 'circle'
73            self.diameter = diameter
74            if height is not None or width is not None:
75                msg = 'Either diameter or width&height must be specified, '
76                msg += 'but not both.'
77                raise Exception, msg
78        else:
79            if height is not None:
80                if width is None:
81                    self.culvert_type = 'square'                               
82                    width = height
83                else:
84                    self.culvert_type = 'rectangle'
85            elif width is not None:
86                if height is None:
87                    self.culvert_type = 'square'                               
88                    height = width
89            else:
90                msg = 'Either diameter or width&height must be specified.'
91                raise Exception, msg               
92               
93            if height == width:
94                self.culvert_type = 'square'                                               
95               
96            self.height = height
97            self.width = width
98
99           
100        assert self.culvert_type in ['circle', 'square', 'rectangle']
101       
102        assert number_of_barrels >= 1
103        self.number_of_barrels = number_of_barrels
104       
105       
106        # Set defaults
107        if manning is None: manning = 0.012   # Default roughness for pipe
108        if loss_exit is None: loss_exit = 1.00
109        if loss_entry is None: loss_entry = 0.50
110        if loss_bend is None: loss_bend=0.00
111        if loss_special is None: loss_special=0.00
112        if blockage_topdwn is None: blockage_topdwn=0.00
113        if blockage_bottup is None: blockage_bottup=0.00
114        if culvert_routine is None: 
115            culvert_routine=boyd_generalised_culvert_model
116           
117        if label is None: label = 'culvert_flow'
118        label += '_' + str(id(self)) 
119        self.label = label
120       
121        # File for storing culvert quantities
122        self.timeseries_filename = label + '_timeseries.csv'
123        fid = open(self.timeseries_filename, 'w')
124        fid.write('time, E0, E1, Velocity, Discharge\n')
125        fid.close()
126
127        # Log file for storing general textual output
128        self.log_filename = label + '.log'         
129        log_to_file(self.log_filename, self.label)       
130        log_to_file(self.log_filename, description)
131        log_to_file(self.log_filename, self.culvert_type)       
132
133
134        # Create the fundamental culvert polygons from POLYGON
135        if self.culvert_type == 'circle':
136            # Redefine width and height for use with create_culvert_polygons
137            width = height = diameter
138       
139        P = create_culvert_polygons(end_point0,
140                                    end_point1,
141                                    width=width,   
142                                    height=height,
143                                    number_of_barrels=number_of_barrels)
144       
145        if verbose is True:
146            pass
147            #plot_polygons([[end_point0, end_point1],
148            #               P['exchange_polygon0'],
149            #               P['exchange_polygon1'],
150            #               P['enquiry_polygon0'],
151            #               P['enquiry_polygon1']],
152            #              figname='culvert_polygon_output')
153            #import sys; sys.exit()                           
154
155
156        # Check that all polygons lie within the mesh.
157        bounding_polygon = domain.get_boundary_polygon()
158        for key in P.keys():
159            if key in ['exchange_polygon0', 
160                       'exchange_polygon1',
161                       'enquiry_polygon0',
162                       'enquiry_polygon1']:
163                for point in P[key]:
164               
165                    msg = 'Point %s in polygon %s for culvert %s did not'\
166                        %(str(point), key, self.label)
167                    msg += 'fall within the domain boundary.'
168                    assert is_inside_polygon(point, bounding_polygon), msg
169       
170
171        # Create inflow object at each end of the culvert.
172        self.openings = []
173        self.openings.append(Inflow(domain,
174                                    polygon=P['exchange_polygon0']))
175
176        self.openings.append(Inflow(domain,
177                                    polygon=P['exchange_polygon1']))                                   
178
179
180        # Assume two openings for now: Referred to as 0 and 1
181        assert len(self.openings) == 2
182       
183        # Store basic geometry
184        self.end_points = [end_point0, end_point1]
185        self.invert_levels = [invert_level0, invert_level1]               
186        #self.enquiry_polygons = [P['enquiry_polygon0'], P['enquiry_polygon1']]
187        self.enquiry_polylines = [P['enquiry_polygon0'][:2], 
188                                  P['enquiry_polygon1'][:2]]
189        self.vector = P['vector']
190        self.length = P['length']; assert self.length > 0.0
191        self.verbose = verbose
192        self.last_time = 0.0       
193       
194
195        # Store hydraulic parameters
196        self.manning = manning
197        self.loss_exit = loss_exit
198        self.loss_entry = loss_entry
199        self.loss_bend = loss_bend
200        self.loss_special = loss_special
201        self.sum_loss = loss_exit + loss_entry + loss_bend + loss_special
202        self.blockage_topdwn = blockage_topdwn
203        self.blockage_bottup = blockage_bottup
204
205        # Store culvert routine
206        self.culvert_routine = culvert_routine
207
208       
209        # Create objects to update momentum (a bit crude at this stage)
210
211       
212        xmom0 = General_forcing(domain, 'xmomentum',
213                                polygon=P['exchange_polygon0'])
214
215        xmom1 = General_forcing(domain, 'xmomentum',
216                                polygon=P['exchange_polygon1'])
217
218        ymom0 = General_forcing(domain, 'ymomentum',
219                                polygon=P['exchange_polygon0'])
220
221        ymom1 = General_forcing(domain, 'ymomentum',
222                                polygon=P['exchange_polygon1'])
223
224        self.opening_momentum = [ [xmom0, ymom0], [xmom1, ymom1] ]
225       
226
227        # Print something
228        s = 'Culvert Effective Length = %.2f m' %(self.length)
229        log_to_file(self.log_filename, s)
230   
231        s = 'Culvert Direction is %s\n' %str(self.vector)
232        log_to_file(self.log_filename, s)       
233       
234    def __call__(self, domain):
235        from anuga.utilities.numerical_tools import mean
236       
237        from anuga.config import g, epsilon
238        from Numeric import take, sqrt
239        from anuga.config import velocity_protection       
240
241
242        log_filename = self.log_filename
243         
244        # Time stuff
245        time = domain.get_time()
246        delta_t = time-self.last_time
247        s = '\nTime = %.2f, delta_t = %f' %(time, delta_t)
248        log_to_file(log_filename, s)
249       
250        msg = 'Time did not advance'
251        if time > 0.0: assert delta_t > 0.0, msg
252
253
254        # Get average water depths at each opening       
255        openings = self.openings   # There are two Opening [0] and [1]
256        for i, opening in enumerate(openings):
257            dq = domain.quantities
258           
259            # Compute mean values of selected quantitites in the exchange area in front of the culvert
260            # Stage and velocity comes from enquiry area and elevation from exchange area
261           
262            stage = dq['stage'].get_values(location='centroids',
263                                           indices=opening.exchange_indices)           
264            w = mean(stage) # Average stage
265
266            # Use invert level instead of elevation if specified
267            invert_level = self.invert_levels[i]
268            if invert_level is not None:
269                z = invert_level
270            else:
271                elevation = dq['elevation'].get_values(location='centroids', indices=opening.exchange_indices)
272                z = mean(elevation) # Average elevation
273
274            # Estimated depth above the culvert inlet
275            d = w - z  # Used for calculations involving depth
276            if d < 0.0:
277                # This is possible since w and z are taken at different locations
278                #msg = 'D < 0.0: %f' %d
279                #raise msg
280                d = 0.0
281           
282
283            # Ratio of depth to culvert height.
284            # If ratio > 1 then culvert is running full
285            if self.culvert_type == 'circle':
286                ratio = d/self.diameter
287            else:   
288                ratio = d/self.height 
289            opening.ratio = ratio
290               
291               
292            # Average measures of energy in front of this opening
293            polyline = self.enquiry_polylines[i]
294            #print 't = %.4f, opening=%d,' %(domain.time, i),
295            opening.total_energy = domain.get_energy_through_cross_section(polyline,
296                                                                           kind='total')           
297            #print 'Et = %.3f m' %opening.total_energy
298
299            # Store current average stage and depth with each opening object
300            opening.depth = d
301            opening.depth_trigger = d # Use this for now
302            opening.stage = w
303            opening.elevation = z
304           
305
306        #################  End of the FOR loop ################################################
307
308           
309        # We now need to deal with each opening individually
310
311        # Determine flow direction based on total energy difference
312        delta_Et = openings[0].total_energy - openings[1].total_energy
313
314        if delta_Et > 0:
315            #print 'Flow U/S ---> D/S'
316            inlet = openings[0]
317            outlet = openings[1]
318
319            inlet.momentum = self.opening_momentum[0]
320            outlet.momentum = self.opening_momentum[1]
321
322        else:
323            #print 'Flow D/S ---> U/S'
324            inlet = openings[1]
325            outlet = openings[0]
326
327            inlet.momentum = self.opening_momentum[1]
328            outlet.momentum = self.opening_momentum[0]
329           
330            delta_Et = -delta_Et
331
332        msg = 'Total energy difference is negative'
333        assert delta_Et > 0.0, msg
334
335        delta_h = inlet.stage - outlet.stage
336        delta_z = inlet.elevation - outlet.elevation
337        culvert_slope = (delta_z/self.length)
338
339        if culvert_slope < 0.0:
340            # Adverse gradient - flow is running uphill
341            # Flow will be purely controlled by uphill outlet face
342            print 'WARNING: Flow is running uphill. Watch Out!', inlet.elevation, outlet.elevation
343
344
345        s = 'Delta total energy = %.3f' %(delta_Et)
346        log_to_file(log_filename, s)
347
348
349        # Calculate discharge for one barrel
350        Q, barrel_velocity, culvert_outlet_depth = self.culvert_routine(self, inlet, outlet, delta_Et, g)
351       
352        # Adjust discharge for multiple barrels
353        Q *= self.number_of_barrels
354
355        # Compute barrel momentum
356        barrel_momentum = barrel_velocity*culvert_outlet_depth
357               
358        s = 'Barrel velocity = %f' %barrel_velocity
359        log_to_file(log_filename, s)
360
361        # Compute momentum vector at outlet
362        outlet_mom_x, outlet_mom_y = self.vector * barrel_momentum
363           
364        s = 'Directional momentum = (%f, %f)' %(outlet_mom_x, outlet_mom_y)
365        log_to_file(log_filename, s)
366
367        # Log timeseries to file
368        fid = open(self.timeseries_filename, 'a')       
369        fid.write('%f, %f, %f, %f, %f\n'\
370                      %(time, 
371                        openings[0].total_energy,
372                        openings[1].total_energy,
373                        barrel_velocity,
374                        Q))
375        fid.close()
376
377        # Update momentum       
378        delta_t = time - self.last_time
379        if delta_t > 0.0:
380            xmomentum_rate = outlet_mom_x - outlet.momentum[0].value
381            xmomentum_rate /= delta_t
382               
383            ymomentum_rate = outlet_mom_y - outlet.momentum[1].value
384            ymomentum_rate /= delta_t
385                   
386            s = 'X Y MOM_RATE = (%f, %f) ' %(xmomentum_rate, ymomentum_rate)
387            log_to_file(log_filename, s)                   
388        else:
389            xmomentum_rate = ymomentum_rate = 0.0
390
391
392        # Set momentum rates for outlet jet
393        outlet.momentum[0].rate = xmomentum_rate
394        outlet.momentum[1].rate = ymomentum_rate
395
396        # Remember this value for next step (IMPORTANT)
397        outlet.momentum[0].value = outlet_mom_x
398        outlet.momentum[1].value = outlet_mom_y                   
399
400        if int(domain.time*100) % 100 == 0:
401            s = 'T=%.5f, Culvert Discharge = %.3f f'\
402                %(time, Q)
403            s += ' Depth= %0.3f  Momentum = (%0.3f, %0.3f)'\
404                 %(culvert_outlet_depth, outlet_mom_x,outlet_mom_y)
405            s += ' Momentum rate: (%.4f, %.4f)'\
406                 %(xmomentum_rate, ymomentum_rate)                   
407            s+='Outlet Vel= %.3f'\
408                %(barrel_velocity)
409            log_to_file(log_filename, s)
410           
411               
412
413
414       
415        # Execute flow term for each opening
416        # This is where Inflow objects are evaluated and update the domain
417        for opening in self.openings:
418            opening(domain)
419
420        # Execute momentum terms
421        # This is where Inflow objects are evaluated and update the domain
422        outlet.momentum[0](domain)
423        outlet.momentum[1](domain)       
424           
425        # Store value of time   
426        self.last_time = time
427
428
Note: See TracBrowser for help on using the repository browser.