source: trunk/anuga_core/source/anuga/structures/structure_operator.py @ 8050

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

Added in an inlet_operator class to implement inflow "boundary condition" by setting up a inlet defined by a line close to the boundary. Look at testing_culvert_inlet.py as an example.

File size: 11.1 KB
Line 
1import anuga
2import numpy as num
3import math
4import inlet_enquiry
5
6from anuga.utilities.system_tools import log_to_file
7
8
9class Structure_operator:
10    """Structure Operator - transfer water from one rectangular box to another.
11    Sets up the geometry of problem
12   
13    This is the base class for structures (culverts, pipes, bridges etc). Inherit from this class (and overwrite
14    discharge_routine method for specific subclasses)
15   
16    Input: Two points, pipe_size (either diameter or width, height),
17    mannings_rougness,
18    """ 
19
20    counter = 0
21
22    def __init__(self,
23                 domain,
24                 end_point0, 
25                 end_point1,
26                 width,
27                 height,
28                 apron,
29                 manning,
30                 enquiry_gap,
31                 description,
32                 label,
33                 structure_type,
34                 logging,
35                 verbose):
36       
37        self.domain = domain
38        self.domain.set_fractional_step_operator(self)
39        self.end_points = [end_point0, end_point1]
40
41       
42       
43        if height is None:
44            height = width
45
46        if apron is None:
47            apron = width
48
49        self.width  = width
50        self.height = height
51        self.apron  = apron
52        self.manning = manning
53        self.enquiry_gap = enquiry_gap
54
55        if description == None:
56            self.description = ' '
57        else:
58            self.description = description
59       
60
61        if label == None:
62            self.label = "structure_%g" % Structure_operator.counter
63        else:
64            self.label = label + '_%g' % Structure_operator.counter
65
66
67        if structure_type == None:
68            self.structure_type = 'generic structure'
69        else:
70            self.structure_type = structure_type
71           
72        self.verbose = verbose
73
74       
75       
76        # Keep count of structures
77        Structure_operator.counter += 1
78
79        # Slots for recording current statistics
80        self.discharge = 0.0
81        self.velocity = 0.0
82        self.delta_total_energy = 0.0
83        self.driving_energy = 0.0
84       
85        self.__create_exchange_polylines()
86
87        self.inlets = []
88        polyline0 = self.inlet_polylines[0]
89        enquiry_point0 = self.inlet_equiry_points[0]
90        outward_vector0 = self.culvert_vector
91        self.inlets.append(inlet_enquiry.Inlet_enquiry(self.domain, polyline0,
92                           enquiry_point0, outward_vector0, self.verbose))
93
94        polyline1 = self.inlet_polylines[1]
95        enquiry_point1 = self.inlet_equiry_points[1]
96        outward_vector1  = - self.culvert_vector
97        self.inlets.append(inlet_enquiry.Inlet_enquiry(self.domain, polyline1,
98                           enquiry_point1, outward_vector1, self.verbose))
99
100        self.set_logging(logging)
101
102    def __call__(self):
103
104        timestep = self.domain.get_timestep()
105       
106        self.determine_inflow_outflow()
107       
108        Q, barrel_speed, outlet_depth = self.discharge_routine()
109
110        old_inflow_height = self.inflow.get_average_height()
111        old_inflow_xmom = self.inflow.get_average_xmom()
112        old_inflow_ymom = self.inflow.get_average_ymom()
113
114        # Implement the update of flow over a timestep by
115        # using a semi-implict update. This ensures that
116        # the update does not create a negative height
117        if old_inflow_height > 0.0 :
118                Q_star = Q/old_inflow_height
119        else:
120                Q_star = 0.0
121
122        factor = 1.0/(1.0 + Q_star*timestep/self.inflow.get_area())
123
124        new_inflow_height = old_inflow_height*factor
125        new_inflow_xmom = old_inflow_xmom*factor
126        new_inflow_ymom = old_inflow_ymom*factor
127           
128        self.inflow.set_heights(new_inflow_height)
129
130        #inflow.set_xmoms(Q/inflow.get_area())
131        #inflow.set_ymoms(0.0)
132
133        self.inflow.set_xmoms(new_inflow_xmom)
134        self.inflow.set_ymoms(new_inflow_ymom)
135
136        loss = (old_inflow_height - new_inflow_height)*self.inflow.get_area()
137
138        # set outflow
139        if old_inflow_height > 0.0 :
140                timestep_star = timestep*new_inflow_height/old_inflow_height
141        else:
142            timestep_star = 0.0
143
144        outflow_extra_height = Q*timestep_star/self.outflow.get_area()
145        outflow_direction = - self.outflow.outward_culvert_vector
146        outflow_extra_momentum = outflow_extra_height*barrel_speed*outflow_direction
147           
148        gain = outflow_extra_height*self.outflow.get_area()
149           
150        #print Q, Q*timestep, barrel_speed, outlet_depth, Qstar, factor, timestep_star
151        #print '  ', loss, gain
152
153        # Stats
154        self.discharge  = Q#outflow_extra_height*self.outflow.get_area()/timestep
155        self.velocity = barrel_speed#self.discharge/outlet_depth/self.width
156
157        new_outflow_height = self.outflow.get_average_height() + outflow_extra_height
158
159        if self.use_momentum_jet :
160            # FIXME (SR) Review momentum to account for possible hydraulic jumps at outlet
161            #new_outflow_xmom = outflow.get_average_xmom() + outflow_extra_momentum[0]
162            #new_outflow_ymom = outflow.get_average_ymom() + outflow_extra_momentum[1]
163
164            new_outflow_xmom = barrel_speed*new_outflow_height*outflow_direction[0]
165            new_outflow_ymom = barrel_speed*new_outflow_height*outflow_direction[1]
166
167        else:
168            #new_outflow_xmom = outflow.get_average_xmom()
169            #new_outflow_ymom = outflow.get_average_ymom()
170
171            new_outflow_xmom = 0.0
172            new_outflow_ymom = 0.0
173
174        self.outflow.set_heights(new_outflow_height)
175        self.outflow.set_xmoms(new_outflow_xmom)
176        self.outflow.set_ymoms(new_outflow_ymom)
177
178
179    def determine_inflow_outflow(self):
180        # Determine flow direction based on total energy difference
181
182        if self.use_velocity_head:
183            self.delta_total_energy = self.inlets[0].get_enquiry_total_energy() - self.inlets[1].get_enquiry_total_energy()
184        else:
185            self.delta_total_energy = self.inlets[0].get_enquiry_stage() - self.inlets[1].get_enquiry_stage()
186
187
188        self.inflow  = self.inlets[0]
189        self.outflow = self.inlets[1]
190       
191
192        if self.delta_total_energy < 0:
193            self.inflow  = self.inlets[1]
194            self.outflow = self.inlets[0]
195            self.delta_total_energy = -self.delta_total_energy
196
197
198    def __create_exchange_polylines(self):
199
200        """Create polylines at the end of a culvert inlet and outlet.
201        At either end two polylines will be created; one for the actual flow to pass through and one a little further away
202        for enquiring the total energy at both ends of the culvert and transferring flow.
203        """
204
205        # Calculate geometry
206        x0, y0 = self.end_points[0]
207        x1, y1 = self.end_points[1]
208
209        dx = x1 - x0
210        dy = y1 - y0
211
212        self.culvert_vector = num.array([dx, dy])
213        self.culvert_length = math.sqrt(num.sum(self.culvert_vector**2))
214        assert self.culvert_length > 0.0, 'The length of culvert is less than 0'
215
216        # Unit direction vector and normal
217        self.culvert_vector /= self.culvert_length                      # Unit vector in culvert direction
218        self.culvert_normal = num.array([-dy, dx])/self.culvert_length  # Normal vector
219
220        # Short hands
221        w = 0.5*self.width*self.culvert_normal # Perpendicular vector of 1/2 width
222        #h = self.apron*self.culvert_vector    # Vector of length=height in the
223                             # direction of the culvert
224
225        #gap = 1.5*h + self.enquiry_gap
226        gap = (self.apron+ self.enquiry_gap)*self.culvert_vector
227
228        self.inlet_polylines = []
229        self.inlet_equiry_points = []
230
231        # Build exchange polyline and enquiry point
232        for i in [0, 1]:
233            i0 = (2*i-1) #i0 determines the sign of the points
234            p0 = self.end_points[i] + w
235            p1 = self.end_points[i] - w
236            ep = self.end_points[i] + i0*gap
237
238            self.inlet_polylines.append(num.array([p0, p1]))
239            self.inlet_equiry_points.append(ep)           
240   
241    def discharge_routine(self):
242       
243        pass
244           
245
246    def statistics(self):
247
248
249        message  = '=====================================\n'
250        message += 'Structure Operator: %s\n' % self.label
251        message += '=====================================\n'
252
253        message += 'Structure Type: %s\n' % self.structure_type
254
255        message += 'Description\n'
256        message += '%s' % self.description
257        message += '\n'
258       
259        for i, inlet in enumerate(self.inlets):
260            message += '-------------------------------------\n'
261            message +=  'Inlet %i\n' % i
262            message += '-------------------------------------\n'
263
264            message += 'inlet triangle indices and centres\n'
265            message += '%s' % inlet.triangle_indices
266            message += '\n'
267           
268            message += '%s' % self.domain.get_centroid_coordinates()[inlet.triangle_indices]
269            message += '\n'
270
271            message += 'polyline\n'
272            message += '%s' % inlet.polyline
273            message += '\n'
274
275        message += '=====================================\n'
276
277        return message
278
279
280    def print_statistics(self):
281
282        print self.statistics()
283
284
285    def print_timestepping_statistics(self):
286
287        message = '---------------------------\n'
288        message += 'Structure report for %s:\n' % self.label
289        message += '--------------------------\n'
290        message += 'Type: %s\n' % self.structure_type
291        message += 'Discharge [m^3/s]: %.2f\n' % self.discharge
292        message += 'Velocity  [m/s]: %.2f\n' % self.velocity
293        message += 'Inlet Driving Energy %.2f\n' % self.driving_energy
294        message += 'Delta Total Energy %.2f\n' % self.delta_total_energy
295        message += 'Control at this instant: %s\n' % self.case
296
297        print message
298
299
300    def set_logging(self, flag=True):
301
302        self.logging = flag
303
304        # If flag is true open file with mode = "w" to form a clean file for logging
305        if self.logging:
306            self.log_filename = self.label + '.log'
307            log_to_file(self.log_filename, self.statistics(), mode='w')
308            log_to_file(self.log_filename, 'time,discharge,velocity,driving_energy,delta_total_energy')
309
310            #log_to_file(self.log_filename, self.culvert_type)
311
312
313    def timestepping_statistics(self):
314
315        message  = '%.5f, ' % self.domain.get_time()
316        message += '%.5f, ' % self.discharge
317        message += '%.5f, ' % self.velocity
318        message += '%.5f, ' % self.driving_energy
319        message += '%.5f' % self.delta_total_energy
320
321        return message
322
323    def log_timestepping_statistics(self):
324
325         if self.logging:
326             log_to_file(self.log_filename, self.timestepping_statistics())
327
328
329    def get_inlets(self):
330       
331        return self.inlets
332       
333       
334    def get_culvert_length(self):
335       
336        return self.culvert_length
337       
338       
339    def get_culvert_width(self):
340       
341        return self.width
342       
343       
344    def get_culvert_diameter(self):
345   
346            return self.width
347       
348       
349    def get_culvert_height(self):
350   
351        return self.height
352
353
354    def get_culvert_apron(self):
355
356        return self.apron
Note: See TracBrowser for help on using the repository browser.