[8004] | 1 | import anuga |
---|
[7993] | 2 | import numpy as num |
---|
| 3 | import math |
---|
[8050] | 4 | import inlet_enquiry |
---|
[7993] | 5 | |
---|
[8018] | 6 | from anuga.utilities.system_tools import log_to_file |
---|
| 7 | |
---|
| 8 | |
---|
[7993] | 9 | class 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 |
---|