Package solvcon :: Module hook
[hide private]
[frames] | no frames]

Source Code for Module solvcon.hook

  1  # -*- coding: UTF-8 -*- 
  2  # 
  3  # Copyright (C) 2008-2010 Yung-Yu Chen <yyc@solvcon.net>. 
  4  # 
  5  # This program is free software; you can redistribute it and/or modify 
  6  # it under the terms of the GNU General Public License as published by 
  7  # the Free Software Foundation; either version 2 of the License, or 
  8  # (at your option) any later version. 
  9  # 
 10  # This program is distributed in the hope that it will be useful, 
 11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 13  # GNU General Public License for more details. 
 14  # 
 15  # You should have received a copy of the GNU General Public License along 
 16  # with this program; if not, write to the Free Software Foundation, Inc., 
 17  # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 
 18   
 19  """ 
 20  Hooks for simulation cases.  Two categories of hooks are defined here: (i) base 
 21  hooks for subclassing and (ii) generic hooks which can be readily installed. 
 22  """ 
23 24 -class Hook(object):
25 """ 26 Organizer class for hooking subroutines for BaseCase. 27 28 @ivar cse: Case object. 29 @itype cse: BaseCase 30 @ivar info: information output function. 31 @itype info: callable 32 @ivar psteps: the interval number of steps between printing. 33 @itype psteps: int 34 @ivar kws: excessive keywords. 35 @itype kws: dict 36 """
37 - def __init__(self, cse, **kw):
38 """ 39 @param cse: Case object. 40 @type cse: BaseCase 41 """ 42 from .case import BaseCase 43 assert isinstance(cse, BaseCase) 44 self.cse = cse 45 self.info = cse.info 46 self.psteps = kw.pop('psteps', None) 47 self.ankcls = kw.pop('ankcls', None) 48 # save excessive keywords. 49 self.kws = dict(kw) 50 super(Hook, self).__init__()
51
52 - def _makedir(self, dirname, verbose=False):
53 """ 54 Make new directory if it does not exist in prior. 55 56 @param dirname: name of directory to be created. 57 @type dirname: str 58 @keyword verbose: flag if print out creation message. 59 @type verbose: bool 60 """ 61 import os 62 if not os.path.exists(dirname): 63 os.makedirs(dirname) 64 if verbose: 65 self.info('Created %s' % dirname)
66
67 - def _depend(self, deplst, verbose=False, stop_on_false=True):
68 """ 69 Check for dependency to another hook. 70 71 @param deplst: list of depended hook classes. 72 @type deplst: list 73 @keyword verbose: flag print message. 74 @type verbose: bool 75 @keyword stop_on_false: flag stop on false. 76 @type stop_on_false: bool 77 @return: dependency met or not. 78 @rtype: bool 79 """ 80 hooks = self.cse.runhooks 81 info = self.info 82 # check. 83 metlst = [] 84 msglst = [] 85 for ahook in deplst: 86 metlst.append(False) 87 for obj in hooks: 88 if isinstance(obj, ahook): 89 metlst[-1] = True 90 break 91 if not metlst[-1]: 92 msglst.append("%s should be enabled for %s." % ( 93 ahook.__name__, self.__class__.__name__)) 94 if verbose and msglst: 95 info('\n'.join(msglst)+'\n') 96 if stop_on_false and msglst: 97 raise RuntimeError, '\n'.join(msglst) 98 # return. 99 for met in metlst: 100 if not met: 101 return False 102 return True
103 104 @staticmethod
105 - def _deliver_anchor(target, ankcls, ankkw):
106 """ 107 Provide the information to instantiate anchor object for a solver. The 108 target object can be a real solver object or a shadow associated to a 109 remote worker object with attached muscle of solver object. 110 111 @param target: the solver or shadow object. 112 @type target: solvcon.solver.Solver or solvcon.rpc.Shadow 113 @param ankcls: type of the anchor to instantiate. 114 @type ankcls: type 115 @param ankkw: keywords to instantiate anchor object. 116 @type ankkw: dict 117 @return: nothing 118 """ 119 from .rpc import Shadow 120 if isinstance(target, Shadow): 121 target.drop_anchor(ankcls, ankkw) 122 else: 123 target.runanchors.append(ankcls, **ankkw)
124 - def drop_anchor(self, svr):
125 """ 126 Drop the anchor(s) to the solver object. 127 128 @param svr: the solver object on which the anchor(s) is dropped. 129 @type svr: solvon.solver.BaseSolver 130 @return: nothing 131 """ 132 if self.ankcls: self._deliver_anchor(svr, self.ankcls, self.kws)
133
134 - def preloop(self):
135 """ 136 Things to do before the time-marching loop. 137 """ 138 pass
139
140 - def premarch(self):
141 """ 142 Things to do before the time march for a specific time step. 143 """ 144 pass
145
146 - def postmarch(self):
147 """ 148 Things to do after the time march for a specific time step. 149 """ 150 pass
151
152 - def postloop(self):
153 """ 154 Things to do after the time-marching loop. 155 """ 156 pass
157
158 -class HookList(list):
159 """ 160 Hook container and invoker. 161 162 @ivar cse: case object. 163 @itype cse: solvcon.case.BaseCase 164 """
165 - def __init__(self, cse, *args, **kw):
166 self.cse = cse 167 super(HookList, self).__init__(*args, **kw)
168 - def append(self, obj, **kw):
169 """ 170 The object to be appended (the first and only argument) should be a 171 Hook object, but this method actually accept either a Hook type or an 172 Anchor type. The method will automatically create the necessary Hook 173 object when detect acceptable type object passed as the first argument. 174 175 All the keywords go to the creation of the Hook object if the first 176 argument is a type. If the first argument is an instantiated Hook 177 object, the method accepts no keywords. 178 179 @param obj: the hook object to be appended. 180 @type obj: solvcon.hook.Hook 181 """ 182 from .anchor import Anchor 183 if isinstance(obj, type): 184 if issubclass(obj, Anchor): 185 kw['ankcls'] = obj 186 obj = Hook 187 obj = obj(self.cse, **kw) 188 else: 189 assert len(kw) == 0 190 super(HookList, self).append(obj)
191 - def __call__(self, method):
192 """ 193 Invoke the specified method for each hook object. 194 195 @param method: name of the method to run. 196 @type method: str 197 """ 198 runhooks = self 199 if method == 'postloop': 200 runhooks = reversed(runhooks) 201 for hook in runhooks: 202 getattr(hook, method)()
203 - def drop_anchor(self, svr):
204 for hok in self: 205 hok.drop_anchor(svr)
206
207 ################################################################################ 208 # Fundamental hooks. 209 ################################################################################ 210 -class ProgressHook(Hook):
211 """ 212 Print simulation progess. 213 214 @ivar linewidth: the maximal width for progress symbol. 50 is upper limit. 215 @itype linewidth: int 216 """
217 - def __init__(self, cse, **kw):
218 self.linewidth = kw.pop('linewidth', 50) 219 super(ProgressHook, self).__init__(cse, **kw)
220 - def preloop(self):
221 istep = self.cse.execution.step_current 222 nsteps = self.cse.execution.steps_run 223 info = self.info 224 info("Steps %d/%d\n" % (istep, nsteps))
225 - def postmarch(self):
226 from time import time 227 istep = self.cse.execution.step_current 228 nsteps = self.cse.execution.steps_run 229 tstart = self.cse.log.time['loop_march'][0] 230 psteps = self.psteps 231 linewidth = self.linewidth 232 info = self.info 233 # calculate estimated remaining time. 234 tcurr = time() 235 tleft = (tcurr-tstart) * ((float(nsteps)-float(istep))/float(istep)) 236 # output information. 237 if istep%psteps == 0: 238 info("#") 239 if istep > 0 and istep%(psteps*linewidth) == 0: 240 info("\nStep %d/%d, %.1fs elapsed, %.1fs left\n" % ( 241 istep, nsteps, tcurr-tstart, tleft, 242 )) 243 elif istep == nsteps: 244 info("\nStep %d/%d done\n" % (istep, nsteps))
245
246 ################################################################################ 247 # Hooks for BlockCase. 248 ################################################################################ 249 -class BlockHook(Hook):
250 """ 251 Base type for hooks needing a BlockCase. 252 """
253 - def __init__(self, cse, **kw):
254 from .case import BlockCase 255 assert isinstance(cse, BlockCase) 256 super(BlockHook, self).__init__(cse, **kw)
257 258 @property
259 - def blk(self):
260 return self.cse.solver.domainobj.blk
261
262 - def _collect_interior(self, key, tovar=False, inder=False, 263 consider_ghost=True):
264 """ 265 @param key: the name of the array to collect in a solver object. 266 @type key: str 267 @keyword tovar: flag to store collect data to case var dict. 268 @type tovar: bool 269 @keyword inder: the array is for derived data. 270 @type inder: bool 271 @keyword consider_ghost: treat the array with the consideration of 272 ghost cells. Default is True. 273 @type consider_ghost: bool 274 @return: the interior array hold by the solver. 275 @rtype: numpy.ndarray 276 """ 277 from numpy import empty 278 cse = self.cse 279 ncell = self.blk.ncell 280 ngstcell = self.blk.ngstcell 281 if cse.is_parallel: 282 dom = self.cse.solver.domainobj 283 # collect arrays from solvers. 284 dealer = self.cse.solver.dealer 285 arrs = list() 286 for iblk in range(dom.nblk): 287 dealer[iblk].cmd.pull(key, inder=inder, with_worker=True) 288 arr = dealer[iblk].recv() 289 arrs.append(arr) 290 # create global array. 291 shape = [it for it in arrs[0].shape] 292 shape[0] = ncell 293 arrg = empty(shape, dtype=arrs[0].dtype) 294 # set global array. 295 clmaps = dom.mappers[2] 296 for iblk in range(dom.nblk): 297 slctg = (clmaps[:,1] == iblk) 298 slctl = clmaps[slctg,0] 299 if consider_ghost: 300 slctl += dom.shapes[iblk,6] 301 arrg[slctg] = arrs[iblk][slctl] 302 else: 303 if consider_ghost: 304 start = ngstcell 305 else: 306 start = 0 307 if inder: 308 arrg = cse.solver.solverobj.der[key][start:].copy() 309 else: 310 arrg = getattr(cse.solver.solverobj, key)[start:].copy() 311 if tovar: 312 self.cse.execution.var[key] = arrg 313 return arrg
314
315 - def _spread_interior(self, arrg, key, consider_ghost=True):
316 """ 317 @param arrg: the global array to be spreaded. 318 @type arrg: numpy.ndarray 319 @param key: the name of the array to collect in a solver object. 320 @type key: str 321 @keyword consider_ghost: treat the arrays with the consideration of 322 ghost cells. Default is True. 323 @type consider_ghost: bool 324 @return: the interior array hold by the solver. 325 @rtype: numpy.ndarray 326 """ 327 from numpy import empty 328 cse = self.cse 329 ncell = self.blk.ncell 330 ngstcell = self.blk.ngstcell 331 if cse.is_parallel: 332 dom = self.cse.solver.domainobj 333 dealer = self.cse.solver.dealer 334 clmaps = dom.mappers[2] 335 for iblk in range(len(dom)): 336 blk = dom[iblk] 337 # create subarray. 338 shape = [it for it in arrg.shape] 339 if consider_ghost: 340 shape[0] = blk.ngstcell+blk.ncell 341 else: 342 shape[0] = blk.ncell 343 arr = empty(shape, dtype=arrg.dtype) 344 # calculate selectors. 345 slctg = (clmaps[:,1] == iblk) 346 slctl = clmaps[slctg,0] 347 if consider_ghost: 348 slctl += blk.ngstcell 349 # push data to remote solver. 350 arr[slctl] = arrg[slctg] 351 dealer[iblk].cmd.push(arr, key, start=blk.ngstcell) 352 else: 353 if consider_ghost: 354 start = ngstcell 355 else: 356 start = 0 357 getattr(cse.solver.solverobj, key)[start:] = arrg[:]
358
359 -class BlockInfoHook(BlockHook):
360 - def __init__(self, cse, **kw):
361 self.show_bclist = kw.pop('show_bclist', False) 362 self.perffn = kw.pop('perffn', None) 363 super(BlockInfoHook, self).__init__(cse, **kw)
364 - def preloop(self):
365 blk = self.blk 366 self.info("Block information:\n %s\n" % str(blk)) 367 if self.show_bclist: 368 for bc in blk.bclist: 369 self.info(" %s\n" % bc)
370 - def postloop(self):
371 import os 372 ncell = self.blk.ncell 373 time = self.cse.log.time['solver_march'] 374 step_init = self.cse.execution.step_init 375 step_current = self.cse.execution.step_current 376 neq = self.cse.execution.neq 377 npart = self.cse.execution.npart 378 # determine filename. 379 perffn = '%s_perf.txt' % self.cse.io.basefn 380 perffn = self.perffn if self.perffn is not None else perffn 381 perffn = os.path.join(self.cse.io.basedir, perffn) 382 pf = open(perffn, 'w') 383 # calculate and output performance. 384 def out(msg): 385 self.info(msg) 386 pf.write(msg)
387 perf = (step_current-step_init)*ncell / time * 1.e-6 388 out('Performance of %s:\n' % self.cse.io.basefn) 389 out(' %g seconds in marching solver.\n' % time) 390 out(' %g microseconds/cell.\n' % (1./perf)) 391 out(' %g Mcells/seconds.\n' % perf) 392 out(' %g Mvariables/seconds.\n' % (perf*neq)) 393 if isinstance(self.cse.execution.npart, int): 394 out(' %g Mcells/seconds/computer.\n' % (perf/npart)) 395 out(' %g Mvariables/seconds/computer.\n' % (perf*neq/npart)) 396 pf.close()
397
398 -class CollectHook(BlockHook):
399 - def __init__(self, cse, **kw):
400 self.varlist = kw.pop('varlist') 401 self.error_on_nan = kw.pop('error_on_nan', False) 402 super(CollectHook, self).__init__(cse, **kw)
403 - def postmarch(self):
404 from numpy import isnan 405 psteps = self.psteps 406 istep = self.cse.execution.step_current 407 if istep%psteps != 0: 408 return 409 vstep = self.cse.execution.varstep 410 var = self.cse.execution.var 411 # collect variables from solver object. 412 if istep != vstep: 413 for key, kw in self.varlist: 414 arr = var[key] = self._collect_interior(key, **kw) 415 nans = isnan(arr) 416 msg = 'nan occurs in %s at step %d' % (key, istep) 417 if nans.any(): 418 if self.error_on_nan: 419 raise ValueError(msg) 420 else: 421 self.info(msg+'\n') 422 self.cse.execution.varstep = istep
423 preloop = postmarch
424
425 ################################################################################ 426 # Markers. 427 ################################################################################ 428 -class SplitMarker(BlockHook):
429 """ 430 Mark each cell with the domain index. 431 """
432 - def preloop(self):
433 from numpy import zeros 434 from .domain import Collective 435 cse = self.cse 436 dom = cse.solver.domainobj 437 if isinstance(dom, Collective): 438 cse.execution.var['domain'] = dom.part 439 else: 440 cse.execution.var['domain'] = zeros(dom.blk.ncell, dtype='int32')
441
442 -class GroupMarker(BlockHook):
443 """ 444 Mark each cell with the group index. 445 """
446 - def preloop(self):
447 var = self.cse.execution.var 448 var['clgrp'] = self.blk.clgrp.copy()
449
450 ################################################################################ 451 # Vtk writers. 452 ################################################################################ 453 -class VtkSave(BlockHook):
454 """ 455 Base type for writer for cse with a block. 456 457 @ivar binary: True for BINARY format; False for ASCII. 458 @itype binary: bool 459 @ivar cache_grid: True to cache grid; False to forget grid every time. 460 @itype cache_grid: bool 461 """
462 - def __init__(self, cse, **kw):
463 self.binary = kw.pop('binary', False) 464 self.cache_grid = kw.pop('cache_grid', True) 465 super(VtkSave, self).__init__(cse, **kw)
466
467 -class SplitSave(VtkSave):
468 """ 469 Save the splitted geometry. 470 """ 471
472 - def preloop(self):
473 from math import log10, ceil 474 from .io.vtk import VtkLegacyUstGridWriter 475 cse = self.cse 476 if cse.is_parallel == 0: 477 return # do nothing if not in parallel. 478 basefn = cse.io.basefn 479 dom = cse.solver.domainobj 480 nblk = len(dom) 481 # build filename templates. 482 vtkfn = basefn + '_decomp' 483 vtksfn_tmpl = basefn + '_decomp' + '_%%0%dd'%int(ceil(log10(nblk))+1) 484 if self.binary: 485 vtkfn += ".bin.vtk" 486 vtksfn_tmpl += ".bin.vtk" 487 else: 488 vtkfn += ".vtk" 489 vtksfn_tmpl += ".vtk" 490 # write. 491 ## lumped. 492 self.info("Save domain decomposition for visualization (%d parts).\n" \ 493 % nblk) 494 VtkLegacyUstGridWriter(dom.blk, 495 binary=self.binary, cache_grid=self.cache_grid).write(vtkfn) 496 ## splitted. 497 iblk = 0 498 for blk in dom: 499 writer = VtkLegacyUstGridWriter(blk, 500 binary=self.binary, cache_grid=self.cache_grid).write( 501 vtksfn_tmpl%iblk) 502 iblk += 1
503
504 -class MarchSave(VtkSave):
505 """ 506 Save the geometry and variables in a case when time marching. 507 508 @ivar vtkfn_tmpl: template for output file name(s). 509 @itype vtkfn_tmpl: str 510 """
511 - def __init__(self, cse, **kw):
512 from math import log10, ceil 513 super(MarchSave, self).__init__(cse, **kw) 514 nsteps = cse.execution.steps_run 515 basefn = cse.io.basefn 516 vtkfn_tmpl = basefn + "_%%0%dd"%int(ceil(log10(nsteps))+1) 517 if self.binary: 518 vtkfn_tmpl += ".bin.vtk" 519 else: 520 vtkfn_tmpl += ".vtk" 521 self.vtkfn_tmpl = kw.pop('vtkfn_tmpl', vtkfn_tmpl)
522 523 @property
524 - def data(self):
525 """ 526 Get dictionaries for scalar and vector data from case. 527 528 @return: dictionaries for scalar and vector. 529 @rtype: tuple 530 """ 531 cse = self.cse 532 exe = cse.execution 533 var = exe.var 534 # create dictionaries for scalars and vectors. 535 sarrs = dict() 536 varrs = dict() 537 for key in var: 538 if key == 'soln' or key == 'dsoln': # skip pure solution. 539 continue 540 arr = var[key] 541 if len(arr.shape) == 1: 542 sarrs[key] = arr 543 elif len(arr.shape) == 2: 544 varrs[key] = arr 545 else: 546 raise IndexError, \ 547 'the dimensions of case[\'%s\'] is %d > 2' % ( 548 key, len(arr.shape)) 549 # put soln into scalars. 550 soln = var['soln'] 551 for i in range(soln.shape[1]): 552 sarrs['soln[%d]'%i] = soln[:,i] 553 # return 554 return sarrs, varrs
555
556 - def _write(self, istep):
557 self.writer.scalars, self.writer.vectors = self.data 558 self.writer.write(self.vtkfn_tmpl % istep)
559
560 - def preloop(self):
561 from .io.vtk import VtkLegacyUstGridWriter 562 psteps = self.psteps 563 cse = self.cse 564 blk = self.blk 565 # initialize writer. 566 self.writer = VtkLegacyUstGridWriter(blk, 567 binary=self.binary, cache_grid=self.cache_grid) 568 # write initially. 569 self._write(0)
570
571 - def postmarch(self):
572 psteps = self.psteps 573 exe = self.cse.execution 574 istep = exe.step_current 575 vstep = exe.varstep 576 if istep%psteps == 0: 577 assert istep == vstep # data must be fresh. 578 self._write(istep)
579