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

Last change on this file since 8050 was 8050, checked in by steve, 12 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
RevLine 
[8004]1import anuga
[7993]2import numpy as num
3import math
[8050]4import inlet_enquiry
[7993]5
[8018]6from anuga.utilities.system_tools import log_to_file
7
8
[7993]9class Structure_operator:
[7995]10    """Structure Operator - transfer water from one rectangular box to another.
[7993]11    Sets up the geometry of problem
12   
[8008]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)
[7993]15   
16    Input: Two points, pipe_size (either diameter or width, height),
17    mannings_rougness,
18    """ 
19
[8018]20    counter = 0
21
[7993]22    def __init__(self,
23                 domain,
24                 end_point0, 
25                 end_point1,
26                 width,
27                 height,
28                 apron,
29                 manning,
30                 enquiry_gap,
[7996]31                 description,
[8018]32                 label,
33                 structure_type,
34                 logging,
[7993]35                 verbose):
36       
37        self.domain = domain
38        self.domain.set_fractional_step_operator(self)
39        self.end_points = [end_point0, end_point1]
[8018]40
[7993]41       
[8018]42       
[7993]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
[8018]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:
[8019]64            self.label = label + '_%g' % Structure_operator.counter
[8018]65
[8019]66
[8018]67        if structure_type == None:
68            self.structure_type = 'generic structure'
69        else:
70            self.structure_type = structure_type
71           
[7993]72        self.verbose = verbose
[7995]73
[8018]74       
75       
76        # Keep count of structures
77        Structure_operator.counter += 1
78
79        # Slots for recording current statistics
[7995]80        self.discharge = 0.0
81        self.velocity = 0.0
[7996]82        self.delta_total_energy = 0.0
83        self.driving_energy = 0.0
[7993]84       
[8049]85        self.__create_exchange_polylines()
[7993]86
87        self.inlets = []
[8048]88        polyline0 = self.inlet_polylines[0]
[7993]89        enquiry_point0 = self.inlet_equiry_points[0]
90        outward_vector0 = self.culvert_vector
[8050]91        self.inlets.append(inlet_enquiry.Inlet_enquiry(self.domain, polyline0,
92                           enquiry_point0, outward_vector0, self.verbose))
[7993]93
[8048]94        polyline1 = self.inlet_polylines[1]
95        enquiry_point1 = self.inlet_equiry_points[1]
[7993]96        outward_vector1  = - self.culvert_vector
[8050]97        self.inlets.append(inlet_enquiry.Inlet_enquiry(self.domain, polyline1,
98                           enquiry_point1, outward_vector1, self.verbose))
[7993]99
[8018]100        self.set_logging(logging)
[8008]101
[7993]102    def __call__(self):
103
[8008]104        timestep = self.domain.get_timestep()
105       
[8035]106        self.determine_inflow_outflow()
[8008]107       
108        Q, barrel_speed, outlet_depth = self.discharge_routine()
[7993]109
[8008]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()
[8024]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
[8008]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
[8035]179    def determine_inflow_outflow(self):
[8008]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
[8049]198    def __create_exchange_polylines(self):
[7993]199
[8048]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
[7993]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
[8048]222        #h = self.apron*self.culvert_vector    # Vector of length=height in the
[7993]223                             # direction of the culvert
224
[8048]225        #gap = 1.5*h + self.enquiry_gap
[8049]226        gap = (self.apron+ self.enquiry_gap)*self.culvert_vector
[7993]227
[8048]228        self.inlet_polylines = []
[7993]229        self.inlet_equiry_points = []
230
[8049]231        # Build exchange polyline and enquiry point
[7993]232        for i in [0, 1]:
[8034]233            i0 = (2*i-1) #i0 determines the sign of the points
[7993]234            p0 = self.end_points[i] + w
235            p1 = self.end_points[i] - w
236            ep = self.end_points[i] + i0*gap
237
[8048]238            self.inlet_polylines.append(num.array([p0, p1]))
239            self.inlet_equiry_points.append(ep)           
[7993]240   
[8008]241    def discharge_routine(self):
242       
243        pass
[7993]244           
245
[8021]246    def statistics(self):
[7993]247
248
[8018]249        message  = '=====================================\n'
250        message += 'Structure Operator: %s\n' % self.label
251        message += '=====================================\n'
[7993]252
[8018]253        message += 'Structure Type: %s\n' % self.structure_type
254
255        message += 'Description\n'
256        message += '%s' % self.description
257        message += '\n'
[7993]258       
259        for i, inlet in enumerate(self.inlets):
[8018]260            message += '-------------------------------------\n'
261            message +=  'Inlet %i\n' % i
262            message += '-------------------------------------\n'
[7993]263
[8018]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'
[7993]270
[8049]271            message += 'polyline\n'
[8048]272            message += '%s' % inlet.polyline
[8018]273            message += '\n'
[7993]274
[8018]275        message += '=====================================\n'
[7995]276
[8018]277        return message
[7995]278
[8018]279
[8021]280    def print_statistics(self):
[8018]281
[8021]282        print self.statistics()
[8018]283
284
285    def print_timestepping_statistics(self):
286
[7995]287        message = '---------------------------\n'
[8018]288        message += 'Structure report for %s:\n' % self.label
[7995]289        message += '--------------------------\n'
[8018]290        message += 'Type: %s\n' % self.structure_type
[7995]291        message += 'Discharge [m^3/s]: %.2f\n' % self.discharge
292        message += 'Velocity  [m/s]: %.2f\n' % self.velocity
[7996]293        message += 'Inlet Driving Energy %.2f\n' % self.driving_energy
[8020]294        message += 'Delta Total Energy %.2f\n' % self.delta_total_energy
[8027]295        message += 'Control at this instant: %s\n' % self.case
[7995]296
[8018]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'
[8021]307            log_to_file(self.log_filename, self.statistics(), mode='w')
[8018]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
[7995]321        return message
322
[8018]323    def log_timestepping_statistics(self):
[8008]324
[8018]325         if self.logging:
326             log_to_file(self.log_filename, self.timestepping_statistics())
327
328
[7993]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       
[7998]344    def get_culvert_diameter(self):
345   
346            return self.width
347       
348       
[7993]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.