[218] | 1 | """Class Domain - 2D triangular domains for finite-volume computations of |
---|
| 2 | the shallow water wave equation |
---|
| 3 | |
---|
| 4 | |
---|
| 5 | Copyright 2004 |
---|
| 6 | Ole Nielsen, Stephen Roberts, Duncan Gray, Christopher Zoppou |
---|
| 7 | Geoscience Australia |
---|
| 8 | """ |
---|
| 9 | |
---|
| 10 | from mesh import Mesh |
---|
| 11 | from generic_boundary_conditions import * |
---|
[590] | 12 | import types |
---|
[218] | 13 | |
---|
| 14 | class Domain(Mesh): |
---|
| 15 | |
---|
| 16 | def __init__(self, coordinates, vertices, boundary = None, |
---|
[415] | 17 | conserved_quantities = None, other_quantities = None, |
---|
| 18 | tagged_elements = None): |
---|
[218] | 19 | |
---|
[415] | 20 | Mesh.__init__(self, coordinates, vertices, boundary, tagged_elements) |
---|
[218] | 21 | |
---|
| 22 | from Numeric import zeros, Float |
---|
[242] | 23 | from quantity import Quantity, Conserved_quantity |
---|
[218] | 24 | |
---|
| 25 | #List of quantity names entering |
---|
| 26 | #the conservation equations |
---|
| 27 | #(Must be a subset of quantities) |
---|
| 28 | if conserved_quantities is None: |
---|
| 29 | self.conserved_quantities = [] |
---|
| 30 | else: |
---|
| 31 | self.conserved_quantities = conserved_quantities |
---|
| 32 | |
---|
| 33 | if other_quantities is None: |
---|
[462] | 34 | self.other_quantities = [] |
---|
| 35 | else: |
---|
| 36 | self.other_quantities = other_quantities |
---|
| 37 | |
---|
[218] | 38 | |
---|
| 39 | #Build dictionary of Quantity instances keyed by quantity names |
---|
| 40 | self.quantities = {} |
---|
[462] | 41 | |
---|
[850] | 42 | #FIXME: remove later - maybe OK, though.... |
---|
[242] | 43 | for name in self.conserved_quantities: |
---|
| 44 | self.quantities[name] = Conserved_quantity(self) |
---|
[462] | 45 | for name in self.other_quantities: |
---|
[218] | 46 | self.quantities[name] = Quantity(self) |
---|
| 47 | |
---|
| 48 | #Create an empty list for explicit forcing terms |
---|
| 49 | self.forcing_terms = [] |
---|
| 50 | |
---|
| 51 | |
---|
| 52 | |
---|
| 53 | #Defaults |
---|
[907] | 54 | from config import max_smallsteps, beta_w, beta_h, epsilon |
---|
| 55 | self.beta_w = beta_w |
---|
| 56 | self.beta_h = beta_h |
---|
[443] | 57 | self.epsilon = epsilon |
---|
[907] | 58 | |
---|
| 59 | #FIXME: Maybe have separate orders for h-limiter and w-limiter? |
---|
| 60 | #Or maybe get rid of order altogether and use beta_w and beta_h |
---|
[218] | 61 | self.default_order = 1 |
---|
| 62 | self.order = self.default_order |
---|
| 63 | self.smallsteps = 0 |
---|
| 64 | self.max_smallsteps = max_smallsteps |
---|
| 65 | self.number_of_steps = 0 |
---|
| 66 | self.number_of_first_order_steps = 0 |
---|
| 67 | |
---|
| 68 | #Model time |
---|
| 69 | self.time = 0.0 |
---|
| 70 | self.finaltime = None |
---|
[566] | 71 | self.min_timestep = self.max_timestep = 0.0 |
---|
[850] | 72 | self.starttime = 0 #Physical starttime if any (0 is 1 Jan 1970 00:00:00) |
---|
[218] | 73 | |
---|
[844] | 74 | #Checkpointing and storage |
---|
| 75 | from config import default_datadir |
---|
| 76 | self.datadir = default_datadir |
---|
[218] | 77 | self.filename = 'domain' |
---|
| 78 | self.checkpoint = False |
---|
| 79 | |
---|
| 80 | |
---|
| 81 | #Public interface to Domain |
---|
| 82 | def get_conserved_quantities(self, vol_id, vertex=None, edge=None): |
---|
| 83 | """Get conserved quantities at volume vol_id |
---|
| 84 | |
---|
| 85 | If vertex is specified use it as index for vertex values |
---|
| 86 | If edge is specified use it as index for edge values |
---|
| 87 | If neither are specified use centroid values |
---|
| 88 | If both are specified an exeception is raised |
---|
| 89 | |
---|
| 90 | Return value: Vector of length == number_of_conserved quantities |
---|
| 91 | |
---|
| 92 | """ |
---|
| 93 | |
---|
| 94 | from Numeric import zeros, Float |
---|
| 95 | |
---|
| 96 | if not (vertex is None or edge is None): |
---|
| 97 | msg = 'Values for both vertex and edge was specified.' |
---|
| 98 | msg += 'Only one (or none) is allowed.' |
---|
| 99 | raise msg |
---|
| 100 | |
---|
| 101 | q = zeros( len(self.conserved_quantities), Float) |
---|
| 102 | |
---|
| 103 | for i, name in enumerate(self.conserved_quantities): |
---|
| 104 | Q = self.quantities[name] |
---|
| 105 | if vertex is not None: |
---|
| 106 | q[i] = Q.vertex_values[vol_id, vertex] |
---|
| 107 | elif edge is not None: |
---|
| 108 | q[i] = Q.edge_values[vol_id, edge] |
---|
| 109 | else: |
---|
| 110 | q[i] = Q.centroid_values[vol_id] |
---|
| 111 | |
---|
| 112 | return q |
---|
| 113 | |
---|
[389] | 114 | |
---|
| 115 | def set_quantity_vertices_dict(self, quantity_dict): |
---|
| 116 | """Set values for named quantities. |
---|
| 117 | The index is the quantity |
---|
| 118 | |
---|
| 119 | name: Name of quantity |
---|
| 120 | X: Compatible list, Numeric array, const or function (see below) |
---|
| 121 | |
---|
| 122 | The values will be stored in elements following their |
---|
| 123 | internal ordering. |
---|
| 124 | |
---|
| 125 | """ |
---|
| 126 | for key in quantity_dict.keys(): |
---|
[462] | 127 | self.set_quantity(key, quantity_dict[key], location='vertices') |
---|
[389] | 128 | |
---|
[659] | 129 | |
---|
[546] | 130 | def set_quantity(self, name, X, location='vertices', indexes = None): |
---|
[218] | 131 | """Set values for named quantity |
---|
| 132 | |
---|
| 133 | name: Name of quantity |
---|
| 134 | X: Compatible list, Numeric array, const or function (see below) |
---|
| 135 | location: Where values are to be stored. |
---|
| 136 | Permissible options are: vertices, edges, centroid |
---|
| 137 | |
---|
| 138 | In case of location == 'centroid' the dimension values must |
---|
| 139 | be a list of a Numerical array of length N, N being the number |
---|
[590] | 140 | of elements. Otherwise it must be of dimension Nx3. |
---|
[546] | 141 | |
---|
| 142 | Indexes is the set of element ids that the operation applies to |
---|
| 143 | |
---|
[218] | 144 | The values will be stored in elements following their |
---|
| 145 | internal ordering. |
---|
[819] | 146 | |
---|
| 147 | #FIXME (Ole): I suggest the following interface |
---|
| 148 | set_quantity(name, X, location, region) |
---|
| 149 | where |
---|
| 150 | name: Name of quantity |
---|
| 151 | X: |
---|
| 152 | -Compatible list, |
---|
| 153 | -Numeric array, |
---|
| 154 | -const or function (see below) |
---|
| 155 | -another quantity Q or an expression of the form |
---|
| 156 | a*Q+b, where a is a scalar or a compatible array or a quantity |
---|
| 157 | Q is a quantity |
---|
| 158 | b is either a scalar, a quantity or a compatible array |
---|
| 159 | location: Where values are to be stored. |
---|
| 160 | Permissible options are: vertices, edges, centroid |
---|
| 161 | region: Identify subset of triangles. Permissible values are |
---|
| 162 | - tag name (refers to tagged region) |
---|
| 163 | - indices (refers to specific triangles) |
---|
| 164 | - polygon (identifies region) |
---|
| 165 | |
---|
| 166 | This should work for all values of X |
---|
| 167 | |
---|
| 168 | |
---|
| 169 | |
---|
[218] | 170 | """ |
---|
| 171 | |
---|
[546] | 172 | #from quantity import Quantity, Conserved_quantity |
---|
[462] | 173 | |
---|
| 174 | #Create appropriate quantity object |
---|
| 175 | ##if name in self.conserved_quantities: |
---|
| 176 | ## self.quantities[name] = Conserved_quantity(self) |
---|
| 177 | ##else: |
---|
| 178 | ## self.quantities[name] = Quantity(self) |
---|
| 179 | |
---|
[546] | 180 | #Set value |
---|
| 181 | self.quantities[name].set_values(X, location, indexes = indexes) |
---|
| 182 | |
---|
| 183 | |
---|
| 184 | def get_quantity(self, name, location='vertices', indexes = None): |
---|
| 185 | """Get values for named quantity |
---|
| 186 | |
---|
| 187 | name: Name of quantity |
---|
| 188 | |
---|
| 189 | In case of location == 'centroid' the dimension values must |
---|
| 190 | be a list of a Numerical array of length N, N being the number |
---|
[590] | 191 | of elements. Otherwise it must be of dimension Nx3. |
---|
[546] | 192 | |
---|
| 193 | Indexes is the set of element ids that the operation applies to. |
---|
[218] | 194 | |
---|
[546] | 195 | The values will be stored in elements following their |
---|
| 196 | internal ordering. |
---|
| 197 | """ |
---|
[218] | 198 | |
---|
[546] | 199 | return self.quantities[name].get_values( location, indexes = indexes) |
---|
| 200 | |
---|
| 201 | |
---|
[218] | 202 | def set_boundary(self, boundary_map): |
---|
| 203 | """Associate boundary objects with tagged boundary segments. |
---|
| 204 | |
---|
| 205 | Input boundary_map is a dictionary of boundary objects keyed |
---|
| 206 | by symbolic tags to matched against tags in the internal dictionary |
---|
| 207 | self.boundary. |
---|
| 208 | |
---|
| 209 | As result one pointer to a boundary object is stored for each vertex |
---|
| 210 | in the list self.boundary_objects. |
---|
| 211 | More entries may point to the same boundary object |
---|
| 212 | |
---|
[648] | 213 | Schematically the mapping is from two dictionaries to one list |
---|
| 214 | where the index is used as pointer to the boundary_values arrays |
---|
| 215 | within each quantity. |
---|
[218] | 216 | |
---|
| 217 | self.boundary: (vol_id, edge_id): tag |
---|
| 218 | boundary_map (input): tag: boundary_object |
---|
| 219 | ---------------------------------------------- |
---|
[648] | 220 | self.boundary_objects: ((vol_id, edge_id), boundary_object) |
---|
[218] | 221 | |
---|
| 222 | |
---|
| 223 | Pre-condition: |
---|
[648] | 224 | self.boundary has been built. |
---|
[218] | 225 | |
---|
| 226 | Post-condition: |
---|
| 227 | self.boundary_objects is built |
---|
| 228 | |
---|
| 229 | If a tag from the domain doesn't appear in the input dictionary an |
---|
| 230 | exception is raised. |
---|
| 231 | However, if a tag is not used to the domain, no error is thrown. |
---|
| 232 | FIXME: This would lead to implementation of a |
---|
[648] | 233 | default boundary condition |
---|
| 234 | |
---|
| 235 | Note: If a segment is listed in the boundary dictionary and if it is |
---|
| 236 | not None, it *will* become a boundary - |
---|
| 237 | even if there is a neighbouring triangle. |
---|
| 238 | This would be the case for internal boundaries |
---|
| 239 | |
---|
| 240 | Boundary objects that are None will be skipped. |
---|
| 241 | |
---|
| 242 | FIXME: If set_boundary is called multiple times and if Boundary |
---|
| 243 | object is changed into None, the neighbour structure will not be |
---|
| 244 | restored!!! |
---|
| 245 | |
---|
| 246 | |
---|
[218] | 247 | """ |
---|
| 248 | |
---|
| 249 | self.boundary_objects = [] |
---|
[648] | 250 | |
---|
| 251 | #FIXME: Try to remove the sorting and fix test_mesh.py |
---|
| 252 | x = self.boundary.keys() |
---|
| 253 | x.sort() |
---|
| 254 | for k, (vol_id, edge_id) in enumerate(x): |
---|
[218] | 255 | tag = self.boundary[ (vol_id, edge_id) ] |
---|
| 256 | |
---|
| 257 | if boundary_map.has_key(tag): |
---|
| 258 | B = boundary_map[tag] |
---|
| 259 | |
---|
[648] | 260 | if B is not None: |
---|
| 261 | self.boundary_objects.append( ((vol_id, edge_id), B) ) |
---|
| 262 | self.neighbours[vol_id, edge_id] = -len(self.boundary_objects) |
---|
| 263 | else: |
---|
| 264 | pass |
---|
| 265 | #FIXME: Check and perhaps fix neighbour structure |
---|
| 266 | |
---|
| 267 | |
---|
[218] | 268 | else: |
---|
| 269 | msg = 'ERROR (domain.py): Tag "%s" has not been ' %tag |
---|
| 270 | msg += 'bound to a boundary object.\n' |
---|
| 271 | msg += 'All boundary tags defined in domain must appear ' |
---|
| 272 | msg += 'in the supplied dictionary.\n' |
---|
| 273 | msg += 'The tags are: %s' %self.get_boundary_tags() |
---|
| 274 | raise msg |
---|
| 275 | |
---|
[851] | 276 | |
---|
[459] | 277 | def set_region(self, functions): |
---|
[469] | 278 | # The order of functions in the list is used. |
---|
[590] | 279 | if type(functions) not in [types.ListType,types.TupleType]: |
---|
| 280 | functions = [functions] |
---|
[469] | 281 | for function in functions: |
---|
| 282 | for tag in self.tagged_elements.keys(): |
---|
[459] | 283 | function(tag, self.tagged_elements[tag], self) |
---|
| 284 | |
---|
| 285 | #Do we need to do this sort of thing? |
---|
| 286 | #self = function(tag, self.tagged_elements[tag], self) |
---|
| 287 | |
---|
[218] | 288 | #MISC |
---|
| 289 | def check_integrity(self): |
---|
| 290 | Mesh.check_integrity(self) |
---|
| 291 | |
---|
| 292 | for quantity in self.conserved_quantities: |
---|
| 293 | msg = 'Conserved quantities must be a subset of all quantities' |
---|
| 294 | assert quantity in self.quantities, msg |
---|
[750] | 295 | |
---|
| 296 | ##assert hasattr(self, 'boundary_objects') |
---|
[218] | 297 | |
---|
| 298 | def write_time(self): |
---|
| 299 | if self.min_timestep == self.max_timestep: |
---|
| 300 | print 'Time = %.4f, delta t = %.8f, steps=%d (%d)'\ |
---|
| 301 | %(self.time, self.min_timestep, self.number_of_steps, |
---|
| 302 | self.number_of_first_order_steps) |
---|
| 303 | elif self.min_timestep > self.max_timestep: |
---|
| 304 | print 'Time = %.4f, steps=%d (%d)'\ |
---|
| 305 | %(self.time, self.number_of_steps, |
---|
| 306 | self.number_of_first_order_steps) |
---|
| 307 | else: |
---|
| 308 | print 'Time = %.4f, delta t in [%.8f, %.8f], steps=%d (%d)'\ |
---|
| 309 | %(self.time, self.min_timestep, |
---|
| 310 | self.max_timestep, self.number_of_steps, |
---|
| 311 | self.number_of_first_order_steps) |
---|
| 312 | |
---|
| 313 | |
---|
[274] | 314 | def get_name(self): |
---|
| 315 | return self.filename |
---|
| 316 | |
---|
[505] | 317 | def set_name(self, name): |
---|
| 318 | self.filename = name |
---|
[218] | 319 | |
---|
[844] | 320 | def get_datadir(self): |
---|
| 321 | return self.datadir |
---|
| 322 | |
---|
| 323 | def set_datadir(self, name): |
---|
| 324 | self.datadir = name |
---|
| 325 | |
---|
| 326 | |
---|
| 327 | |
---|
[462] | 328 | #def set_defaults(self): |
---|
| 329 | # """Set default values for uninitialised quantities. |
---|
| 330 | # Should be overridden or specialised by specific modules |
---|
| 331 | # """# |
---|
| 332 | # |
---|
| 333 | # for name in self.conserved_quantities + self.other_quantities: |
---|
| 334 | # self.set_quantity(name, 0.0) |
---|
| 335 | |
---|
[218] | 336 | |
---|
| 337 | ########################### |
---|
| 338 | #Main components of evolve |
---|
| 339 | |
---|
| 340 | def evolve(self, yieldstep = None, finaltime = None): |
---|
| 341 | """Evolve model from time=0.0 to finaltime yielding results |
---|
| 342 | every yieldstep. |
---|
| 343 | |
---|
| 344 | Internally, smaller timesteps may be taken. |
---|
| 345 | |
---|
| 346 | Evolve is implemented as a generator and is to be called as such, e.g. |
---|
| 347 | |
---|
| 348 | for t in domain.evolve(timestep, yieldstep, finaltime): |
---|
| 349 | <Do something with domain and t> |
---|
| 350 | |
---|
| 351 | """ |
---|
| 352 | |
---|
| 353 | from config import min_timestep, max_timestep, epsilon |
---|
| 354 | |
---|
[750] | 355 | #FIXME: Maybe lump into a larger check prior to evolving |
---|
| 356 | msg = 'Boundary tags must be bound to boundary objects before evolving system, ' |
---|
| 357 | msg += 'e.g. using the method set_boundary.\n' |
---|
| 358 | msg += 'This system has the boundary tags %s ' %self.get_boundary_tags() |
---|
| 359 | assert hasattr(self, 'boundary_objects'), msg |
---|
| 360 | |
---|
[462] | 361 | ##self.set_defaults() |
---|
| 362 | |
---|
[218] | 363 | if yieldstep is None: |
---|
| 364 | yieldstep = max_timestep |
---|
| 365 | |
---|
| 366 | self.order = self.default_order |
---|
| 367 | |
---|
[462] | 368 | |
---|
[218] | 369 | self.yieldtime = 0.0 #Time between 'yields' |
---|
| 370 | |
---|
| 371 | #Initialise interval of timestep sizes (for reporting only) |
---|
| 372 | self.min_timestep = max_timestep |
---|
| 373 | self.max_timestep = min_timestep |
---|
| 374 | self.finaltime = finaltime |
---|
| 375 | self.number_of_steps = 0 |
---|
| 376 | self.number_of_first_order_steps = 0 |
---|
| 377 | |
---|
| 378 | #Initial update of vertex and edge values |
---|
| 379 | self.distribute_to_vertices_and_edges() |
---|
| 380 | |
---|
| 381 | |
---|
| 382 | #Or maybe restore from latest checkpoint |
---|
| 383 | if self.checkpoint is True: |
---|
| 384 | self.goto_latest_checkpoint() |
---|
[269] | 385 | |
---|
[218] | 386 | |
---|
| 387 | yield(self.time) #Yield initial values |
---|
| 388 | |
---|
| 389 | while True: |
---|
| 390 | #Update boundary values |
---|
| 391 | self.update_boundary() |
---|
[232] | 392 | |
---|
[269] | 393 | #Compute fluxes across each element edge |
---|
[218] | 394 | self.compute_fluxes() |
---|
| 395 | |
---|
| 396 | #Update timestep to fit yieldstep and finaltime |
---|
| 397 | self.update_timestep(yieldstep, finaltime) |
---|
| 398 | |
---|
| 399 | #Update conserved quantities |
---|
| 400 | self.update_conserved_quantities() |
---|
| 401 | |
---|
| 402 | #Update vertex and edge values |
---|
| 403 | self.distribute_to_vertices_and_edges() |
---|
[229] | 404 | |
---|
[218] | 405 | #Update time |
---|
| 406 | self.time += self.timestep |
---|
| 407 | self.yieldtime += self.timestep |
---|
| 408 | self.number_of_steps += 1 |
---|
| 409 | if self.order == 1: |
---|
| 410 | self.number_of_first_order_steps += 1 |
---|
| 411 | |
---|
| 412 | #Yield results |
---|
| 413 | if finaltime is not None and abs(self.time - finaltime) < epsilon: |
---|
[269] | 414 | # Yield final time and stop |
---|
[218] | 415 | yield(self.time) |
---|
| 416 | break |
---|
[269] | 417 | |
---|
[218] | 418 | |
---|
| 419 | if abs(self.yieldtime - yieldstep) < epsilon: |
---|
| 420 | # Yield (intermediate) time and allow inspection of domain |
---|
| 421 | |
---|
| 422 | if self.checkpoint is True: |
---|
| 423 | self.store_checkpoint() |
---|
| 424 | self.delete_old_checkpoints() |
---|
| 425 | |
---|
[269] | 426 | #Pass control on to outer loop for more specific actions |
---|
[218] | 427 | yield(self.time) |
---|
| 428 | |
---|
| 429 | # Reinitialise |
---|
| 430 | self.yieldtime = 0.0 |
---|
| 431 | self.min_timestep = max_timestep |
---|
| 432 | self.max_timestep = min_timestep |
---|
| 433 | self.number_of_steps = 0 |
---|
| 434 | self.number_of_first_order_steps = 0 |
---|
| 435 | |
---|
| 436 | |
---|
| 437 | def evolve_to_end(self, finaltime = 1.0): |
---|
[274] | 438 | """Iterate evolve all the way to the end |
---|
[218] | 439 | """ |
---|
| 440 | |
---|
| 441 | for _ in self.evolve(yieldstep=None, finaltime=finaltime): |
---|
| 442 | pass |
---|
| 443 | |
---|
| 444 | |
---|
| 445 | |
---|
| 446 | def update_boundary(self): |
---|
| 447 | """Go through list of boundary objects and update boundary values |
---|
| 448 | for all conserved quantities on boundary. |
---|
| 449 | """ |
---|
| 450 | |
---|
| 451 | #FIXME: Update only those that change (if that can be worked out) |
---|
[648] | 452 | for i, ((vol_id, edge_id), B) in enumerate(self.boundary_objects): |
---|
[218] | 453 | q = B.evaluate(vol_id, edge_id) |
---|
| 454 | |
---|
| 455 | for j, name in enumerate(self.conserved_quantities): |
---|
| 456 | Q = self.quantities[name] |
---|
| 457 | Q.boundary_values[i] = q[j] |
---|
| 458 | |
---|
| 459 | |
---|
| 460 | def compute_fluxes(self): |
---|
| 461 | msg = 'Method compute_fluxes must be overridden by Domain subclass' |
---|
| 462 | raise msg |
---|
| 463 | |
---|
| 464 | |
---|
| 465 | def update_timestep(self, yieldstep, finaltime): |
---|
| 466 | |
---|
| 467 | from config import min_timestep |
---|
| 468 | |
---|
| 469 | timestep = self.timestep |
---|
| 470 | |
---|
| 471 | #Record maximal and minimal values of timestep for reporting |
---|
| 472 | self.max_timestep = max(timestep, self.max_timestep) |
---|
| 473 | self.min_timestep = min(timestep, self.min_timestep) |
---|
| 474 | |
---|
| 475 | #Protect against degenerate time steps |
---|
| 476 | if timestep < min_timestep: |
---|
| 477 | |
---|
| 478 | #Number of consecutive small steps taken b4 taking action |
---|
| 479 | self.smallsteps += 1 |
---|
| 480 | |
---|
| 481 | if self.smallsteps > self.max_smallsteps: |
---|
| 482 | self.smallsteps = 0 #Reset |
---|
| 483 | |
---|
| 484 | if self.order == 1: |
---|
[443] | 485 | msg = 'Too small timestep %.16f reached ' %timestep |
---|
| 486 | msg += 'even after %d steps of 1 order scheme'\ |
---|
| 487 | %self.max_smallsteps |
---|
[218] | 488 | |
---|
| 489 | raise msg |
---|
| 490 | else: |
---|
| 491 | #Try to overcome situation by switching to 1 order |
---|
| 492 | self.order = 1 |
---|
| 493 | |
---|
| 494 | else: |
---|
| 495 | self.smallsteps = 0 |
---|
[443] | 496 | if self.order == 1 and self.default_order == 2: |
---|
| 497 | self.order = 2 |
---|
[218] | 498 | |
---|
| 499 | |
---|
| 500 | #Ensure that final time is not exceeded |
---|
| 501 | if finaltime is not None and self.time + timestep > finaltime: |
---|
| 502 | timestep = finaltime-self.time |
---|
| 503 | |
---|
| 504 | #Ensure that model time is aligned with yieldsteps |
---|
| 505 | if self.yieldtime + timestep > yieldstep: |
---|
| 506 | timestep = yieldstep-self.yieldtime |
---|
| 507 | |
---|
| 508 | self.timestep = timestep |
---|
| 509 | |
---|
| 510 | |
---|
| 511 | |
---|
| 512 | def compute_forcing_terms(self): |
---|
| 513 | """If there are any forcing functions driving the system |
---|
[274] | 514 | they should be defined in Domain subclass and appended to |
---|
| 515 | the list self.forcing_terms |
---|
[218] | 516 | """ |
---|
| 517 | |
---|
| 518 | for f in self.forcing_terms: |
---|
| 519 | f(self) |
---|
| 520 | |
---|
[458] | 521 | |
---|
[218] | 522 | |
---|
| 523 | def update_conserved_quantities(self): |
---|
| 524 | """Update vectors of conserved quantities using previously |
---|
| 525 | computed fluxes specified forcing functions. |
---|
| 526 | """ |
---|
| 527 | |
---|
| 528 | from Numeric import ones, sum, equal, Float |
---|
| 529 | |
---|
| 530 | N = self.number_of_elements |
---|
| 531 | d = len(self.conserved_quantities) |
---|
| 532 | |
---|
| 533 | timestep = self.timestep |
---|
| 534 | |
---|
| 535 | #Compute forcing terms |
---|
| 536 | self.compute_forcing_terms() |
---|
| 537 | |
---|
[458] | 538 | #Update conserved_quantities |
---|
[218] | 539 | for name in self.conserved_quantities: |
---|
| 540 | Q = self.quantities[name] |
---|
| 541 | Q.update(timestep) |
---|
| 542 | |
---|
| 543 | #Clean up |
---|
[274] | 544 | #Note that Q.explicit_update is reset by compute_fluxes |
---|
[218] | 545 | Q.semi_implicit_update[:] = 0.0 |
---|
[274] | 546 | |
---|
[218] | 547 | |
---|
| 548 | |
---|
| 549 | def distribute_to_vertices_and_edges(self): |
---|
| 550 | """Extrapolate conserved quantities from centroid to |
---|
| 551 | vertices and edge-midpoints for each volume |
---|
| 552 | |
---|
| 553 | Default implementation is straight first order, |
---|
| 554 | i.e. constant values throughout each element and |
---|
| 555 | no reference to non-conserved quantities. |
---|
| 556 | """ |
---|
| 557 | |
---|
| 558 | for name in self.conserved_quantities: |
---|
| 559 | Q = self.quantities[name] |
---|
| 560 | if self.order == 1: |
---|
| 561 | Q.extrapolate_first_order() |
---|
| 562 | elif self.order == 2: |
---|
| 563 | Q.extrapolate_second_order() |
---|
| 564 | Q.limit() |
---|
| 565 | else: |
---|
| 566 | raise 'Unknown order' |
---|
| 567 | Q.interpolate_from_vertices_to_edges() |
---|
| 568 | |
---|
| 569 | |
---|
| 570 | |
---|
| 571 | ############################################## |
---|
| 572 | #Initialise module |
---|
| 573 | |
---|
[240] | 574 | #Optimisation with psyco |
---|
| 575 | from config import use_psyco |
---|
| 576 | if use_psyco: |
---|
| 577 | try: |
---|
| 578 | import psyco |
---|
| 579 | except: |
---|
| 580 | msg = 'WARNING: psyco (speedup) could not import'+\ |
---|
| 581 | ', you may want to consider installing it' |
---|
| 582 | print msg |
---|
| 583 | else: |
---|
| 584 | psyco.bind(Domain.update_boundary) |
---|
[443] | 585 | #psyco.bind(Domain.update_timestep) #Not worth it |
---|
[240] | 586 | psyco.bind(Domain.update_conserved_quantities) |
---|
[278] | 587 | psyco.bind(Domain.distribute_to_vertices_and_edges) |
---|
| 588 | |
---|
[240] | 589 | |
---|
| 590 | if __name__ == "__main__": |
---|
| 591 | pass |
---|