source: branches/source_numpy_conversion/anuga/culvert_flows/culvert_class.py @ 6768

Last change on this file since 6768 was 5918, checked in by rwilson, 16 years ago

NumPy? conversion.

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