1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 """
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
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
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
99 for met in metlst:
100 if not met:
101 return False
102 return True
103
104 @staticmethod
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)
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
135 """
136 Things to do before the time-marching loop.
137 """
138 pass
139
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
159 """
160 Hook container and invoker.
161
162 @ivar cse: case object.
163 @itype cse: solvcon.case.BaseCase
164 """
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)
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)()
206
211 """
212 Print simulation progess.
213
214 @ivar linewidth: the maximal width for progress symbol. 50 is upper limit.
215 @itype linewidth: int
216 """
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
234 tcurr = time()
235 tleft = (tcurr-tstart) * ((float(nsteps)-float(istep))/float(istep))
236
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
250 """
251 Base type for hooks needing a BlockCase.
252 """
257
258 @property
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
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
291 shape = [it for it in arrs[0].shape]
292 shape[0] = ncell
293 arrg = empty(shape, dtype=arrs[0].dtype)
294
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
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
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
345 slctg = (clmaps[:,1] == iblk)
346 slctl = clmaps[slctg,0]
347 if consider_ghost:
348 slctl += blk.ngstcell
349
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
361 self.show_bclist = kw.pop('show_bclist', False)
362 self.perffn = kw.pop('perffn', None)
363 super(BlockInfoHook, self).__init__(cse, **kw)
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
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
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
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
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
429 """
430 Mark each cell with the domain index.
431 """
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
443 """
444 Mark each cell with the group index.
445 """
447 var = self.cse.execution.var
448 var['clgrp'] = self.blk.clgrp.copy()
449
450
451
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 """
463 self.binary = kw.pop('binary', False)
464 self.cache_grid = kw.pop('cache_grid', True)
465 super(VtkSave, self).__init__(cse, **kw)
466
468 """
469 Save the splitted geometry.
470 """
471
473 from math import log10, ceil
474 from .io.vtk import VtkLegacyUstGridWriter
475 cse = self.cse
476 if cse.is_parallel == 0:
477 return
478 basefn = cse.io.basefn
479 dom = cse.solver.domainobj
480 nblk = len(dom)
481
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
491
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
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
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 """
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
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
535 sarrs = dict()
536 varrs = dict()
537 for key in var:
538 if key == 'soln' or key == 'dsoln':
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
550 soln = var['soln']
551 for i in range(soln.shape[1]):
552 sarrs['soln[%d]'%i] = soln[:,i]
553
554 return sarrs, varrs
555
557 self.writer.scalars, self.writer.vectors = self.data
558 self.writer.write(self.vtkfn_tmpl % istep)
559
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
578 self._write(istep)
579