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

Source Code for Module solvcon.anchor

  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  Anchors attached to the solvers.  There's only one base anchor class for 
 21  subclassing.  Any other anchors defined here are for directly installation. 
 22   
 23  A special GlueAnchor is defined to glue two collocated BCs.  The pair of glued 
 24  BCs works as an internal interface.  As such, the BCs can be dynamically turn 
 25  on or off. 
 26  """ 
27 28 -class Anchor(object):
29 """ 30 Anchor that called by solver objects at various stages. 31 32 @ivar svr: the solver object to be attached to. 33 @itype svr: solvcon.solver.Solver 34 @ivar kws: excessive keywords. 35 @itype kws: dict 36 """ 37
38 - def __init__(self, svr, **kw):
39 from .solver import BaseSolver 40 assert isinstance(svr, BaseSolver) 41 self.svr = svr 42 self.kws = dict(kw)
43
44 - def provide(self):
45 pass
46 - def preloop(self):
47 pass
48 - def premarch(self):
49 pass
50 - def prefull(self):
51 pass
52 - def presub(self):
53 pass
54 - def postsub(self):
55 pass
56 - def postfull(self):
57 pass
58 - def postmarch(self):
59 pass
60 - def postloop(self):
61 pass
62 - def exhaust(self):
63 pass
64
65 -class AnchorList(list):
66 """ 67 Anchor container and invoker. 68 69 @ivar svr: solver object. 70 @itype svr: solvcon.solver.BaseSolver 71 """
72 - def __init__(self, svr, *args, **kw):
73 self.svr = svr 74 self.names = dict() 75 super(AnchorList, self).__init__(*args, **kw)
76 - def append(self, obj, **kw):
77 name = kw.pop('name', None) 78 if isinstance(name, int): 79 raise ValueError('name can\'t be integer') 80 if isinstance(obj, type): 81 obj = obj(self.svr, **kw) 82 super(AnchorList, self).append(obj) 83 if name != None: 84 self.names[name] = obj
85 - def __getitem__(self, key):
86 if key in self.names: 87 return self.names[key] 88 else: 89 return super(AnchorList, self).__getitem__(key)
90 - def __call__(self, method):
91 """ 92 Invoke the specified method for each anchor. 93 94 @param method: name of the method to run. 95 @type method: str 96 @return: nothing 97 """ 98 runanchors = self.svr.runanchors 99 if method == 'postloop' or method == 'exhaust': 100 runanchors = reversed(runanchors) 101 for anchor in runanchors: 102 func = getattr(anchor, method, None) 103 if func != None: 104 func()
105
106 ################################################################################ 107 # Solution output. 108 ################################################################################ 109 110 -class MarchSaveAnchor(Anchor):
111 """ 112 Save solution data into VTK XML format for a solver. 113 114 @ivar anames: the arrays in der of solvers to be saved. True means in der. 115 @itype anames: dict 116 @ivar compressor: compressor for binary data. Can only be 'gz' or ''. 117 @itype compressor: str 118 @ivar fpdtype: string for floating point data type (in numpy convention). 119 @itype fpdtype: str 120 @ivar psteps: the interval (in step) to save data. 121 @itype psteps: int 122 @ivar vtkfn_tmpl: the template string for the VTK file. 123 @itype vtkfn_tmpl: str 124 """
125 - def __init__(self, svr, **kw):
126 self.anames = kw.pop('anames', dict()) 127 self.compressor = kw.pop('compressor') 128 self.fpdtype = kw.pop('fpdtype') 129 self.psteps = kw.pop('psteps') 130 self.vtkfn_tmpl = kw.pop('vtkfn_tmpl') 131 super(MarchSaveAnchor, self).__init__(svr, **kw)
132 - def _write(self, istep):
133 from .io.vtkxml import VtkXmlUstGridWriter 134 from .solver import FakeBlockVtk 135 ngstcell = self.svr.ngstcell 136 sarrs = dict() 137 varrs = dict() 138 # collect data. 139 for key in self.anames: 140 # get the array. 141 if self.anames[key]: 142 arr = self.svr.der[key][ngstcell:] 143 else: 144 arr = getattr(self.svr, key)[ngstcell:] 145 # put array in dict. 146 if len(arr.shape) == 1: 147 sarrs[key] = arr 148 elif arr.shape[1] == self.svr.ndim: 149 varrs[key] = arr 150 else: 151 for it in range(arr.shape[1]): 152 sarrs['%s[%d]' % (key, it)] = arr[:,it] 153 # write. 154 wtr = VtkXmlUstGridWriter(FakeBlockVtk(self.svr), fpdtype=self.fpdtype, 155 compressor=self.compressor, scalars=sarrs, vectors=varrs) 156 svrn = self.svr.svrn 157 wtr.write(self.vtkfn_tmpl % (istep if svrn is None else (istep, svrn)))
158 - def preloop(self):
159 self._write(0)
160 - def postmarch(self):
161 psteps = self.psteps 162 istep = self.svr.step_global 163 if istep%psteps == 0: 164 self._write(istep)
165 - def postloop(self):
166 psteps = self.psteps 167 istep = self.svr.step_global 168 if istep%psteps != 0: 169 self._write(istep)
170
171 ################################################################################ 172 # Anchors for in situ visualization. 173 ################################################################################ 174 175 -class VtkAnchor(Anchor):
176 """ 177 Abstract class for VTK filtering anchor. Must override process() method 178 for use. Note: svr.ust is shared by all VtkAnchor instances. 179 180 @ivar anames: the arrays in der of solvers to be saved. True means in der. 181 @itype anames: dict 182 @ivar fpdtype: string for floating point data type (in numpy convention). 183 @itype fpdtype: str 184 @ivar psteps: the interval (in step) to save data. 185 @itype psteps: int 186 @ivar vtkfn_tmpl: the template string for the VTK file. 187 @itype vtkfn_tmpl: str 188 """
189 - def __init__(self, svr, **kw):
190 self.anames = kw.pop('anames', dict()) 191 self.fpdtype = kw.pop('fpdtype') 192 self.psteps = kw.pop('psteps') 193 self.vtkfn_tmpl = kw.pop('vtkfn_tmpl') 194 self.vac = dict() 195 super(VtkAnchor, self).__init__(svr, **kw)
196 @property
197 - def vtkfn(self):
198 """ 199 The correct file name for VTK based on the template. 200 """ 201 istep = self.svr.step_global 202 svrn = self.svr.svrn 203 return self.vtkfn_tmpl % ( 204 istep if svrn is None else (istep, svrn))
205 - def _aggregate(self):
206 """ 207 Aggregate data from solver object to VTK unstructured mesh. 208 209 @return: nothing 210 """ 211 from .visual_vtk import valid_vector, set_array 212 ngstcell = self.svr.ngstcell 213 fpdtype = self.fpdtype 214 ust = self.svr.ust 215 # collect derived. 216 for key, inder, flag in self.anames: 217 # get the array. 218 if inder: 219 arr = self.svr.der[key][ngstcell:] 220 else: 221 arr = getattr(self.svr, key)[ngstcell:] 222 # set array in unstructured mesh. 223 if len(arr.shape) == 1: 224 set_array(arr, key, fpdtype, ust) 225 elif arr.shape[1] == self.svr.ndim: 226 set_array(valid_vector(arr), key, fpdtype, ust) 227 else: 228 for it in range(arr.shape[1]): 229 set_array(arr[:,it], '%s[%d]' % (key, it), fpdtype, ust)
230 - def preloop(self):
231 self.process(0)
232 - def postmarch(self):
233 psteps = self.psteps 234 istep = self.svr.step_global 235 if istep%psteps == 0: 236 self.process(istep)
237 - def postloop(self):
238 psteps = self.psteps 239 istep = self.svr.step_global 240 if istep%psteps != 0: 241 self.process(istep)
242 - def process(self, istep):
243 """ 244 This method implements the VTK filtering operations. Must be 245 overidden. 246 """ 247 raise NotImplementedError
248
249 ################################################################################ 250 # StatAnchor. 251 ################################################################################ 252 253 -class RuntimeStatAnchor(Anchor):
254 """ 255 Report the Linux load average through solver. Reports are made after a 256 full marching interation. 257 258 @ivar reports: list what should be reported. Default is ['loadavg'] only. 259 @itype reports: list 260 @ivar cputotal: flag to use total jiffy for cpu usage percentage. 261 @itype cputotal: bool 262 @ivar cputime: marker for timing cpu usage. 263 @itype cputime: float 264 @ivar jiffytime: the time a jiffy is. Default is 0.01 second. 265 @itype jiffytime: float 266 """ 267 268 PSTAT_KEYS = [ 269 ('pid', int), ('comm', str), ('state', str), 270 ('ppid', int), ('pgrp', int), ('session', int), 271 ('tty_nr', int), ('tpgid', int), ('flags', int), 272 ('minflt', int), ('cminflt', int), ('majflt', int), ('cmajflt', int), 273 ('utime', int), ('stime', int), ('cutime', int), ('cstime', int), 274 ('priority', int), ('nice', int), ('num_threads', int), 275 ('itrealvalue', int), ('starttime', int), 276 ('vsize', int), ('rss', int), ('rsslim', int), 277 ('startcode', int), ('endcode', int), ('startstack', int), 278 ('kstkesp', int), ('kstkeip', int), 279 # obselete, use /proc/[pid]/status. 280 ('signal', int), ('blocked', int), ('sigignore', int), 281 ('sigcatch', int), 282 # process waiting channel. 283 ('wchan', int), 284 # not maintained. 285 ('nswap', int), ('cnswap', int), 286 # signal to parent process when die. 287 ('exit_signal', int), 288 ('processor', int), ('rt_priority', int), 289 ('policy', int), ('delayacct_blkio_ticks', int), 290 ('guest_time', int), ('cguest_time', int), 291 ] 292 293 SETTING_KEYS = ['ibcthread'] 294 ENVAR_KEYS = ['KMP_AFFINITY'] 295 296 CPU_NAMES = ['us', 'sy', 'ni', 'id', 'wa', 'hi', 'si', 'st'] 297 298 @classmethod
299 - def get_pstat(cls):
300 import os 301 pid = os.getpid() 302 f = open('/proc/%d/stat'%pid) 303 sinfo = f.read().split() 304 f.close() 305 pstat = dict() 306 for it in range(len(sinfo)): 307 key, typ = cls.PSTAT_KEYS[it] 308 pstat[key] = typ(sinfo[it]) 309 return pstat
310 311 @staticmethod
312 - def get_cpu_frame():
313 f = open('/proc/stat') 314 totcpu = f.readlines()[0] 315 f.close() 316 return [float(it) for it in totcpu.split()[1:]]
317 318 @staticmethod
319 - def calc_cpu_difference(frame0, frame1):
320 frame = list() 321 for it in range(len(frame0)): 322 frame.append(frame1[it]-frame0[it]) 323 return frame
324 325 @classmethod
326 - def get_envar(cls):
327 import os 328 envar = dict() 329 for key in cls.ENVAR_KEYS: 330 envar[key] = os.environ.get(key, None) 331 return envar
332 333 @staticmethod
334 - def get_loadavg():
335 f = open('/proc/loadavg') 336 loadavg = f.read() 337 f.close() 338 return [float(val) for val in loadavg.split()[:3]]
339
340 - def __init__(self, svr, **kw):
341 self.cputotal = kw.pop('cputotal', True) 342 self.jiffytime = kw.pop('jiffytime', 0.01) 343 self.records = list() 344 super(RuntimeStatAnchor, self).__init__(svr, **kw)
345
346 - def _get_record(self):
347 from time import time 348 record = dict() 349 record['time'] = time() 350 pstat = self.get_pstat() 351 cpu = self.get_cpu_frame() 352 loadavg = self.get_loadavg() 353 # pstat. 354 for key in ['utime', 'stime', 'priority', 'nice', 'num_threads', 355 'vsize', 'rss', 'rt_priority']: 356 record[key] = pstat[key] 357 # cpu usage. 358 record['cpu'] = cpu 359 # loadavg. 360 record['loadavg'] = loadavg 361 # envar. 362 record.update(self.get_envar()) 363 # timer. 364 record.update(self.svr.timer) 365 return record
366
367 - def _msg_setting(self, record):
368 return ' '.join([ 369 '%s=%s' % (key, str(getattr(self.svr, key))) for key in 370 self.SETTING_KEYS 371 ])
372
373 - def _msg_envar(self, record):
374 return ' '.join([ 375 '%s=%s' % (key, str(record[key])) for key in self.ENVAR_KEYS 376 ])
377
378 - def _msg_cpu(self, record):
379 # get the difference to the frame since last run of this method. 380 if len(self.records): 381 oldtime = self.records[-1]['time'] 382 framediff = self.calc_cpu_difference( 383 self.records[-1]['cpu'], record['cpu']) 384 pcpudiff = [ 385 record['utime'] - self.records[-1]['utime'], 386 record['stime'] - self.records[-1]['stime'], 387 ] 388 else: 389 oldtime = record['time'] 390 framediff = record['cpu'] 391 pcpudiff = [record['utime'], record['stime']] 392 # calculate the percentage. 393 if self.cputotal: 394 jiffy = sum(framediff) 395 else: 396 jiffy = (record['time']-oldtime)/self.jiffytime 397 if jiffy == 0.0: jiffy = 1.e100 398 pscale = [it/jiffy*100 for it in pcpudiff] 399 oscale = [it/jiffy*100 for it in framediff] 400 # build message. 401 process = '%.2f%%utime %.2f%%stime' % (pscale[0], pscale[1]) 402 overall = ' '.join(['%s%s' % ('%.2f%%'%oscale[it], self.CPU_NAMES[it]) 403 for it in range(len(self.CPU_NAMES)) 404 ]) 405 return ' '.join(['cputotal=%s'%self.cputotal, process, overall])
406 @staticmethod
407 - def _parse_cpu(line):
408 every = line.split() 409 time = float(every[0]) 410 return [time] + [float(tok.split('%')[0]) for tok in every[2:]]
411 @classmethod
412 - def plot_cpu(cls, lines, ax, xtime=False, showx=True, 413 lloc='right'):
414 arr, xval, xlabel = cls._parse(lines, 'cpu', xtime) 415 arr[0,:] = arr[1,:] 416 ax.plot(xval, arr[:,2:5].sum(axis=1), '-', label='us+st+ni') 417 ax.plot(xval, arr[:,5:7].sum(axis=1), ':', label='id+wa') 418 ax.plot(xval, arr[:,0:2].sum(axis=1), '--', label='utime+stime') 419 if showx: ax.set_xlabel(xlabel) 420 ax.set_ylabel('CPU %') 421 ax.set_ylim([0,100]) 422 ax.legend(loc=lloc)
423
424 - def _msg_mem(self, record):
425 return '%d' % record['vsize']
426 @staticmethod
427 - def _parse_mem(line):
428 time, vsize = line.split() 429 return [float(time), int(vsize)]
430 @classmethod
431 - def plot_mem(cls, lines, ax, xtime=False, showx=True, 432 lloc=None):
433 arr, xval, xlabel = cls._parse(lines, 'mem', xtime) 434 ax.plot(xval, arr[:,0]/1024**2, '-') 435 if showx: ax.set_xlabel(xlabel) 436 ax.set_ylabel('Memory usage (MB)')
437
438 - def _msg_loadavg(self, record):
439 return '%.2f %.2f %.2f' % tuple(record['loadavg'])
440 @staticmethod
441 - def _parse_loadavg(line):
442 return [float(val) for val in line.split()]
443 @classmethod
444 - def plot_loadavg(cls, lines, ax, xtime=False, showx=True, 445 lloc='right'):
446 arr, xval, xlabel = cls._parse(lines, 'loadavg', xtime) 447 ax.plot(xval, arr[:,0], '-', label='1 min') 448 ax.plot(xval, arr[:,1], ':', label='5 min') 449 ax.plot(xval, arr[:,2], '--', label='15 min') 450 if showx: ax.set_xlabel(xlabel) 451 ax.set_ylabel('Load average') 452 ax.legend(loc=lloc)
453 454 @classmethod
455 - def _parse(cls, lines, key, xtime):
456 from numpy import array, arange 457 myhead = 'RT_%s: ' % key 458 nmyhead = len(myhead) 459 mymethod = getattr(cls, '_parse_%s' % key) 460 data = list() 461 for line in lines: 462 loc = line.find(myhead) 463 if loc > -1: 464 data.append(mymethod(line[loc+nmyhead:])) 465 arr = array(data, dtype='float64') 466 xval = arr[:,0]-arr[0,0] if xtime else arange(arr.shape[0])+1 467 xlabel = 'Time (s)' if xtime else 'Steps' 468 return arr[:,1:].copy(), xval.copy(), xlabel
469
470 - def postfull(self):
471 import sys 472 if not sys.platform.startswith('linux'): return 473 rec = self._get_record() 474 # output the messages. 475 time = rec['time'] 476 for mkey in ['cpu', 'loadavg', 'mem', 'setting', 'envar']: 477 method = getattr(self, '_msg_%s'%mkey) 478 self.svr.mesg('RT_%s: %.20e %s\n' % (mkey, time, method(rec))) 479 # save. 480 self.records.append(rec)
481
482 -class MarchStatAnchor(Anchor):
483 """ 484 Report the time used in each methods of marching. 485 """ 486 487 @classmethod
488 - def plot(cls, keys, lines, ax, lloc='best'):
489 maxavg = 0.0 490 for key in keys: 491 arr, xval, xlabel = cls.parse(lines, key) 492 ax.plot(xval, arr[:,0], label='%s'%key) 493 maxavg = max(arr[:,0].mean(), maxavg) 494 ax.set_xlabel(xlabel) 495 ax.set_ylabel('Time (s)') 496 ax.set_ylim([0.0, maxavg*1.5]) 497 ax.legend(loc=lloc)
498 499 @classmethod
500 - def parse(cls, lines, key, diff=True):
501 from numpy import array, arange 502 myhead = 'MA_%s: ' % key 503 nmyhead = len(myhead) 504 data = list() 505 for line in lines: 506 loc = line.find(myhead) 507 if loc > -1: 508 data.append([float(val) for val in line[loc+nmyhead:].split()]) 509 arr = array(data, dtype='float64') 510 if diff: 511 arr[1:,:] = arr[1:,:] - arr[:-1,:] 512 arr[0,:] = arr[1,:] 513 xval = arange(arr.shape[0])+1 514 xlabel = 'Steps' 515 return arr, xval.copy(), xlabel
516
517 - def postfull(self):
518 for key in ['march'] + self.svr.mmnames: 519 val = self.svr.timer.get(key, None) 520 if val == None: 521 continue 522 self.svr.mesg('MA_%s: %g\n' % (key, val))
523
524 -class TpoolStatAnchor(Anchor):
525 """ 526 Report the ticks used in each threads in pool. 527 """ 528 529 @classmethod
530 - def plot(cls, key, lines, ax, showx=True, lloc='best'):
531 arr, xval, xlabel = cls._parse(lines, key) 532 for it in range(arr.shape[1]): 533 ax.plot(xval, arr[:,it], label='%s%d'%(key, it)) 534 if showx: ax.set_xlabel(xlabel) 535 ax.set_ylabel('CPU ticks') 536 ax.legend(loc=lloc)
537 538 @classmethod
539 - def _parse(cls, lines, key):
540 from numpy import array, arange 541 myhead = 'TP_%s: ' % key 542 nmyhead = len(myhead) 543 data = list() 544 for line in lines: 545 loc = line.find(myhead) 546 if loc > -1: 547 data.append([int(val) for val in line[loc+nmyhead:].split()]) 548 arr = array(data, dtype='int32') 549 arr[1:,:] = arr[1:,:] - arr[:-1,:] 550 arr[0,:] = arr[1,:] 551 xval = arange(arr.shape[0])+1 552 xlabel = 'Steps' 553 return arr, xval.copy(), xlabel
554
555 - def postfull(self):
556 for key in self.svr.mmnames: 557 if key not in self.svr.ticker: 558 continue 559 vals = self.svr.ticker[key] 560 nval = len(vals) 561 self.svr.mesg('TP_%s: %s\n' % ( 562 key, ' '.join(['%d'%val for val in vals]) 563 ))
564
565 ################################################################################ 566 # Initialization. 567 ################################################################################ 568 569 -class FillAnchor(Anchor):
570 """ 571 Fill the array with value. 572 """
573 - def __init__(self, svr, **kw):
574 self.keys = kw.pop('keys') 575 self.value = kw.pop('value') 576 super(FillAnchor, self).__init__(svr, **kw)
577 - def provide(self):
578 for key in self.keys: 579 getattr(self.svr, key).fill(self.value)
580
581 ################################################################################ 582 # Glue. 583 ################################################################################ 584 585 -class GlueAnchor(Anchor):
586 """ 587 Use Glue class to glue specified BC objects of a solver object. 588 589 @cvar KEYS_ENABLER: names of the arrays that should be modified when 590 enabling/disabling the glue. 591 @ctype KEYS_ENABLER: sequence 592 @ivar bcpairs: a sequence of 2-tuples for BC object pairs to be glued. 593 @itype bcpairs: sequence 594 """ 595 596 KEYS_ENABLER = tuple() 597
598 - def __init__(self, svr, **kw):
599 self.bcpairs = kw.pop('bcpairs') 600 super(GlueAnchor, self).__init__(svr, **kw)
601
602 - def _attach_glue(self):
603 """ 604 Attach Glue objects to specified BC object pairs. 605 606 @return: nothing 607 """ 608 from .boundcond import Glue 609 nmbc = dict([(bc.name, bc) for bc in self.svr.bclist]) 610 for key0, key1 in self.bcpairs: 611 assert nmbc[key0].glue is None 612 assert nmbc[key1].glue is None 613 Glue(nmbc[key0], nmbc[key1])
614
615 - def _detach_glue(self):
616 """ 617 Detach Glue objects from specified BC object pairs. 618 619 @return: nothing 620 """ 621 from .boundcond import Glue 622 nmbc = dict([(bc.name, bc) for bc in self.svr.bclist]) 623 for key0, key1 in self.bcpairs: 624 assert isinstance(nmbc[key0].glue, Glue) 625 assert isinstance(nmbc[key1].glue, Glue) 626 nmbc[key0].glue = None 627 nmbc[key1].glue = None
628
629 - def _enable_glue(self, check=True):
630 """ 631 Enable the gluing mechanism by calling Glue.enable() for specified BC 632 object pairs. 633 634 @keyword check: check Glue object or not. Default True. 635 @type check: bool 636 @return: nothing 637 """ 638 from ctypes import byref 639 svr = self.svr 640 if check: 641 self._attach_glue() 642 nmbc = dict([(bc.name, bc) for bc in svr.bclist]) 643 for keys in self.bcpairs: 644 for key in keys: 645 nmbc[key].glue.enable(*self.KEYS_ENABLER) 646 svr._clib_cuse_c.prepare_ce(byref(svr.exd)) 647 svr._clib_cuse_c.prepare_sf(byref(svr.exd)) 648 if svr.scu: svr.cumgr.arr_to_gpu()
649
650 - def _disable_glue(self, check=True):
651 """ 652 Disable the gluing mechanism by calling Glue.disable() for specified BC 653 object pairs. 654 655 @keyword check: check Glue object or not. Default True. 656 @type check: bool 657 @return: nothing 658 """ 659 from ctypes import byref 660 svr = self.svr 661 nmbc = dict([(bc.name, bc) for bc in svr.bclist]) 662 for keys in self.bcpairs: 663 for key in keys: 664 nmbc[key].glue.disable(*self.KEYS_ENABLER) 665 svr._clib_cuse_c.prepare_ce(byref(svr.exd)) 666 svr._clib_cuse_c.prepare_sf(byref(svr.exd)) 667 if svr.scu: svr.cumgr.arr_to_gpu() 668 if check: 669 self._detach_glue()
670