source: trunk/anuga_core/source/anuga/structures/culvert_operator.py @ 7975

Last change on this file since 7975 was 7975, checked in by steve, 14 years ago

Got a working culvert!

File size: 10.0 KB
Line 
1import sys
2
3from anuga.shallow_water.forcing import Inflow, General_forcing
4from anuga.utilities.system_tools import log_to_file
5from anuga.geometry.polygon import inside_polygon, is_inside_polygon
6from anuga.geometry.polygon import plot_polygons, polygon_area
7
8from anuga.utilities.numerical_tools import mean
9from anuga.utilities.numerical_tools import ensure_numeric, sign
10       
11from anuga.config import g, epsilon
12from anuga.config import minimum_allowed_height, velocity_protection       
13import anuga.utilities.log as log
14
15import inlet
16
17import numpy as num
18import math
19
20class Below_interval(Exception): pass 
21class Above_interval(Exception): pass
22
23
24class Generic_box_culvert:
25    """Culvert flow - transfer water from one rectangular box to another.
26    Sets up the geometry of problem
27   
28    This is the base class for culverts. Inherit from this class (and overwrite
29    compute_discharge method for specific subclasses)
30   
31    Input: Two points, pipe_size (either diameter or width, height),
32    mannings_rougness,
33    """ 
34
35    def __init__(self,
36                 domain,
37                 end_point0=None, 
38                 end_point1=None,
39                 enquiry_gap_factor=0.2,
40                 width=None,
41                 height=None,
42                 verbose=False):
43       
44        # Input check
45       
46        self.domain = domain
47
48        self.domain.set_fractional_step_operator(self)
49
50        self.end_points = [end_point0, end_point1]
51        self.enquiry_gap_factor = enquiry_gap_factor
52       
53        if height is None:
54            height = width
55
56        self.width = width
57        self.height = height
58       
59        self.verbose=verbose
60        self.filename = None
61       
62        # Create the fundamental culvert polygons and create inlet objects
63        self.create_culvert_polygons()
64
65        #FIXME (SR) Put this into a foe loop to deal with more inlets
66        self.inlets = []
67        polygon0 = self.inlet_polygons[0]
68        enquiry_pt0 = self.enquiry_points[0]
69        inlet0_vector = self.culvert_vector
70        self.inlets.append(inlet.Inlet(self.domain, polygon0, enquiry_pt0, inlet0_vector))
71
72        polygon1 = self.inlet_polygons[1]
73        enquiry_pt1 = self.enquiry_points[1]
74        inlet1_vector = - self.culvert_vector
75        self.inlets.append(inlet.Inlet(self.domain, polygon1, enquiry_pt1, inlet1_vector))
76 
77
78   
79        self.print_stats()
80
81
82    def __call__(self):
83
84        # Time stuff
85        time     = self.domain.get_time()
86        timestep = self.domain.get_timestep()
87
88
89
90        inflow  = self.inlets[0]
91        outflow = self.inlets[1]
92
93        # Determine flow direction based on total energy difference
94        delta_total_energy = inflow.get_average_total_energy() - outflow.get_average_total_energy()
95
96        if delta_total_energy < 0:
97            inflow  = self.inlets[1]
98            outflow = self.inlets[0]
99            delta_total_energy = -delta_total_energy
100
101        delta_z = inflow.get_average_elevation() - outflow.get_average_elevation()
102        culvert_slope = delta_z/self.culvert_length
103
104        # Determine controlling energy (driving head) for culvert
105        if inflow.get_average_specific_energy() > delta_total_energy:
106            # Outlet control
107            driving_head = delta_total_energy
108        else:
109            # Inlet control
110            driving_head = inflow.get_average_specific_energy()
111           
112
113        # Transfer
114        from culvert_routines import boyd_generalised_culvert_model
115        Q, barrel_velocity, culvert_outlet_depth =\
116                    boyd_generalised_culvert_model(inflow.get_average_height(),
117                                         outflow.get_average_height(),
118                                         inflow.get_average_speed(),
119                                         outflow.get_average_speed(),
120                                         inflow.get_average_specific_energy(),
121                                         delta_total_energy,
122                                         g,
123                                         culvert_length=self.culvert_length,
124                                         culvert_width=self.width,
125                                         culvert_height=self.height,
126                                         culvert_type='box',
127                                         manning=0.01)
128
129        transfer_water = Q*timestep
130
131
132        inflow.set_heights(inflow.get_average_height() - transfer_water)
133        inflow.set_xmoms(0.0)
134        inflow.set_ymoms(0.0)
135
136
137        outflow.set_heights(outflow.get_average_height() + transfer_water)
138        outflow.set_xmoms(0.0)
139        outflow.set_ymoms(0.0)
140
141
142
143    def print_stats(self):
144
145        print '====================================='
146        print 'Generic Culvert Operator'
147        print '====================================='
148        print "enquiry_gap_factor"
149        print self.enquiry_gap_factor
150       
151        for i, inlet in enumerate(self.inlets):
152            print '-------------------------------------'
153            print 'Inlet %i' % i
154            print '-------------------------------------'
155
156            print 'inlet triangle indices and centres'
157            print inlet.triangle_indices[i]
158            print self.domain.get_centroid_coordinates()[inlet.triangle_indices[i]]
159       
160            print 'polygon'
161            print inlet.polygon
162
163            print 'enquiry_point'
164            print inlet.enquiry_point
165
166        print '====================================='
167
168
169
170
171
172    def create_culvert_polygons(self):
173
174        """Create polygons at the end of a culvert inlet and outlet.
175        At either end two polygons will be created; one for the actual flow to pass through and one a little further away
176        for enquiring the total energy at both ends of the culvert and transferring flow.
177        """
178
179        # Calculate geometry
180        x0, y0 = self.end_points[0]
181        x1, y1 = self.end_points[1]
182
183        dx = x1 - x0
184        dy = y1 - y0
185
186        self.culvert_vector = num.array([dx, dy])
187        self.culvert_length = math.sqrt(num.sum(self.culvert_vector**2))
188        assert self.culvert_length > 0.0, 'The length of culvert is less than 0'
189
190        # Unit direction vector and normal
191        self.culvert_vector /= self.culvert_length                      # Unit vector in culvert direction
192        self.culvert_normal = num.array([-dy, dx])/self.culvert_length  # Normal vector
193
194        # Short hands
195        w = 0.5*self.width*self.culvert_normal # Perpendicular vector of 1/2 width
196        h = self.height*self.culvert_vector    # Vector of length=height in the
197                             # direction of the culvert
198        gap = (1 + self.enquiry_gap_factor)*h
199
200        self.inlet_polygons = []
201        self.enquiry_points = []
202
203        # Build exchange polygon and enquiry points 0 and 1
204        for i in [0, 1]:
205            i0 = (2*i-1)
206            p0 = self.end_points[i] + w
207            p1 = self.end_points[i] - w
208            p2 = p1 + i0*h
209            p3 = p0 + i0*h
210            self.inlet_polygons.append(num.array([p0, p1, p2, p3]))
211            self.enquiry_points.append(self.end_points[i] + i0*gap)
212
213        # Check that enquiry points are outside inlet polygons
214        for i in [0,1]:
215            polygon = self.inlet_polygons[i]
216            # FIXME (SR) Probably should calculate the area of all the triangles
217            # associated with this polygon, as there is likely to be some
218            # inconsistency between triangles and ploygon
219            area = polygon_area(polygon)
220           
221
222            msg = 'Polygon %s ' %(polygon)
223            msg += ' has area = %f' % area
224            assert area > 0.0, msg
225
226            for j in [0,1]:
227                point = self.enquiry_points[j]
228                msg = 'Enquiry point falls inside a culvert polygon.'
229
230                assert not inside_polygon(point, polygon), msg
231
232   
233
234                       
235# FIXME(Ole): Write in C and reuse this function by similar code
236# in interpolate.py
237def interpolate_linearly(x, xvec, yvec):
238
239    msg = 'Input to function interpolate_linearly could not be converted '
240    msg += 'to numerical scalar: x = %s' % str(x)
241    try:
242        x = float(x)
243    except:
244        raise Exception, msg
245
246
247    # Check bounds
248    if x < xvec[0]:
249        msg = 'Value provided = %.2f, interpolation minimum = %.2f.'\
250            % (x, xvec[0])
251        raise Below_interval, msg
252
253    if x > xvec[-1]:
254        msg = 'Value provided = %.2f, interpolation maximum = %.2f.'\
255            %(x, xvec[-1])
256        raise Above_interval, msg
257
258
259    # Find appropriate slot within bounds
260    i = 0
261    while x > xvec[i]: i += 1
262
263
264    x0 = xvec[i-1]
265    x1 = xvec[i]
266    alpha = (x - x0)/(x1 - x0)
267
268    y0 = yvec[i-1]
269    y1 = yvec[i]
270    y = alpha*y1 + (1-alpha)*y0
271
272    return y
273
274
275
276def read_culvert_description(culvert_description_filename):
277
278    # Read description file
279    fid = open(culvert_description_filename)
280
281    read_rating_curve_data = False
282    rating_curve = []
283    for i, line in enumerate(fid.readlines()):
284
285        if read_rating_curve_data is True:
286            fields = line.split(',')
287            head_difference = float(fields[0].strip())
288            flow_rate = float(fields[1].strip())
289            barrel_velocity = float(fields[2].strip())
290
291            rating_curve.append([head_difference, flow_rate, barrel_velocity])
292
293        if i == 0:
294            # Header
295            continue
296        if i == 1:
297            # Metadata
298            fields = line.split(',')
299            label=fields[0].strip()
300            type=fields[1].strip().lower()
301            assert type in ['box', 'pipe']
302
303            width=float(fields[2].strip())
304            height=float(fields[3].strip())
305            length=float(fields[4].strip())
306            number_of_barrels=int(fields[5].strip())
307            #fields[6] refers to losses
308            description=fields[7].strip()
309
310        if line.strip() == '': continue # Skip blanks
311
312        if line.startswith('Rating'):
313            read_rating_curve_data = True
314            # Flow data follows
315
316    fid.close()
317
318    return label, type, width, height, length, number_of_barrels, description, rating_curve
319
Note: See TracBrowser for help on using the repository browser.