def __call__(self, intg): if intg.tcurr - self.tout_last < self.dt_out - self.tol: return stats = Inifile() stats.set('data', 'fields', ','.join(self.fields)) stats.set('data', 'prefix', 'soln') intg.collect_stats(stats) # Prepare the metadata metadata = dict(intg.cfgmeta, stats=stats.tostr(), mesh_uuid=intg.mesh_uuid) # Extract and subset the solution soln = [intg.soln[i][..., rgn] for i, rgn in self._ele_regions] # Add in any required region data data = self._add_region_data(soln) # Write out the file solnfname = self._writer.write(data, metadata, intg.tcurr) # If a post-action has been registered then invoke it self._invoke_postaction(mesh=intg.system.mesh.fname, soln=solnfname, t=intg.tcurr) # Update the last output time self.tout_last = intg.tcurr
def __call__(self, intg): if intg.tcurr - self.tout_last < self.dt_out - self.tol: return stats = Inifile() stats.set('data', 'fields', ','.join(self.fields)) stats.set('data', 'prefix', 'soln') intg.collect_stats(stats) # Prepare the metadata metadata = dict(intg.cfgmeta, stats=stats.tostr(), mesh_uuid=intg.mesh_uuid) # Prepare the data itself data = self._prepare_data(intg) # Write out the file solnfname = self._writer.write(data, metadata, intg.tcurr) # If a post-action has been registered then invoke it self._invoke_postaction(mesh=intg.system.mesh.fname, soln=solnfname, t=intg.tcurr) # Update the last output time self.tout_last = intg.tcurr
def __call__(self, intg): dowrite = abs(self.tout - intg.tcurr) < self.tol doaccum = intg.nacptsteps % self.nsteps == 0 if dowrite or doaccum: # Evaluate the time averaging expressions currex = self._eval_exprs(intg) # Accumulate them; always do this even when just writing for a, p, c in zip(self.accmex, self.prevex, currex): a += 0.5 * (intg.tcurr - self.prevt) * (p + c) # Save the time and solution self.prevt = intg.tcurr self.prevex = currex if dowrite: # Normalise accmex = [a / self.dtout for a in self.accmex] stats = Inifile() stats.set('data', 'prefix', 'tavg') stats.set('data', 'fields', ','.join(k for k, v in self.exprs)) stats.set('tavg', 'tstart', intg.tcurr - self.dtout) stats.set('tavg', 'tend', intg.tcurr) intg.collect_stats(stats) metadata = dict(config=self.cfg.tostr(), stats=stats.tostr(), mesh_uuid=intg.mesh_uuid) self._writer.write(accmex, metadata, intg.tcurr) self.tout = intg.tcurr + self.dtout self.accmex = [np.zeros_like(a) for a in accmex]
def __call__(self, intg): if abs(self.tout_next - intg.tcurr) > intg.dtmin: return stats = Inifile() stats.set('data', 'fields', ','.join(self.fields)) stats.set('data', 'prefix', 'soln') intg.collect_stats(stats) metadata = dict(config=self.cfg.tostr(), stats=stats.tostr(), mesh_uuid=intg.mesh_uuid) self._writer.write(intg.soln, metadata, intg.tcurr) self.tout_next += self.dt_out
def __call__(self, intg): if intg.tcurr - self.tout_last < self.dt_out - self.tol: return comm, rank, root = get_comm_rank_root() # If we are the root rank then prepare the metadata if rank == root: stats = Inifile() stats.set('data', 'fields', ','.join(self.fields)) stats.set('data', 'prefix', 'soln') intg.collect_stats(stats) metadata = dict(intg.cfgmeta, stats=stats.tostr(), mesh_uuid=intg.mesh_uuid) else: metadata = None # Fetch data from other plugins and add it to metadata with ad-hoc keys for csh in intg.completed_step_handlers: try: prefix = intg.get_plugin_data_prefix(csh.name, csh.suffix) pdata = csh.serialise(intg) except AttributeError: pdata = {} if rank == root: metadata |= {f'{prefix}/{k}': v for k, v in pdata.items()} # Fetch and (if necessary) subset the solution data = dict(self._ele_region_data) for idx, etype, rgn in self._ele_regions: data[etype] = intg.soln[idx][..., rgn].astype(self.fpdtype) # Write out the file solnfname = self._writer.write(data, intg.tcurr, metadata) # If a post-action has been registered then invoke it self._invoke_postaction(intg=intg, mesh=intg.system.mesh.fname, soln=solnfname, t=intg.tcurr) # Update the last output time self.tout_last = intg.tcurr
def process_tavg(args): infs = {} # Interrogate files passed by the shell for fname in args.infs: # Load solution files and obtain solution times inf = read_pyfr_data(fname) tinf = Inifile(inf['stats']).getfloat('solver-time-integrator', 'tcurr') # Retain if solution time is within limits if args.limits is None or args.limits[0] <= tinf <= args.limits[1]: infs[tinf] = inf # Verify that solutions were computed on the same mesh if inf['mesh_uuid'] != infs[infs.keys()[0]]['mesh_uuid']: raise RuntimeError('Solution files in scope were not computed ' 'on the same mesh') # Sort the solution times, check for sufficient files in scope stimes = sorted(infs.keys()) if len(infs) <= 1: raise RuntimeError('More than one solution file is required to ' 'compute an average') # Initialise progress bar, and the average with first solution pb = ProgressBar(0, 0, len(stimes), 0) avgs = {name: infs[stimes[0]][name].copy() for name in infs[stimes[0]]} solnfs = [name for name in avgs.keys() if name.startswith('soln')] # Weight the initialised trapezoidal mean dtnext = stimes[1] - stimes[0] for name in solnfs: avgs[name] *= 0.5 * dtnext pb.advance_to(1) # Compute the trapezoidal mean up to the last solution file for i in xrange(len(stimes[2:])): dtlast = dtnext dtnext = stimes[i + 2] - stimes[i + 1] # Weight the current solution, then add to the mean for name in solnfs: avgs[name] += 0.5 * (dtlast + dtnext) * infs[stimes[i + 1]][name] pb.advance_to(i + 2) # Weight final solution, update mean and normalise for elapsed time for name in solnfs: avgs[name] += 0.5 * dtnext * infs[stimes[-1]][name] avgs[name] *= 1.0 / (stimes[-1] - stimes[0]) pb.advance_to(i + 3) # Compute and assign stats for a time-averaged solution stats = Inifile() stats.set('time-average', 'tmin', stimes[0]) stats.set('time-average', 'tmax', stimes[-1]) stats.set('time-average', 'ntlevels', len(stimes)) avgs['stats'] = stats.tostr() outf = open(args.outf, 'wb') np.savez(outf, **avgs)
def test_hex_gleg_ord3(): # Config for a third order DG scheme cfg = Inifile() cfg.set('solver', 'order', '3') cfg.set('solver-interfaces-quad', 'flux-pts', 'gauss-legendre') cfg.set('solver-elements-hex', 'soln-pts', 'gauss-legendre') # Generate the shape hs = HexShape(None, cfg) # Load and import the reference values fobj = BytesIO(pkgutil.get_data(__name__, 'hex-gleg-ord3.npz')) refm = np.load(fobj) assert np.allclose(refm['m0'], hs.m0) assert np.allclose(refm['m1'], hs.m1) assert np.allclose(refm['m2'], hs.m2) assert np.allclose(refm['m3'], hs.m3)
def save_solution(self, savedir, basename, t=0): ndims = self.solver.system.ndims nvars = self.solver.system.nvars writer = NativeWriter(self.solver, nvars, savedir, basename, prefix='soln') fields = self.solver.system.elementscls.convarmap[ndims] stats = Inifile() stats.set('data', 'fields', ','.join(fields)) stats.set('data', 'prefix', 'soln') self.solver.collect_stats(stats) stats.set('solver-time-integrator', 'tcurr', str(t)) metadata = dict(self.solver.cfgmeta, stats=stats.tostr(), mesh_uuid=self.solver.mesh_uuid) writer.write(self.solver.soln, metadata, t)
def __call__(self, intg): # If we are not supposed to be averaging yet then return if intg.tcurr < self.tstart: return # If necessary, run the start-up routines if not self._started: self._init_accumex(intg) self._started = True # See if we are due to write and/or accumulate this step dowrite = intg.tcurr - self.tout_last >= self.dtout - self.tol doaccum = intg.nacptsteps % self.nsteps == 0 if dowrite or doaccum: # Evaluate the time averaging expressions currex = self._eval_acc_exprs(intg) # Accumulate them; always do this even when just writing for a, p, c in zip(self.accex, self.prevex, currex): a += 0.5*(intg.tcurr - self.prevt)*(p + c) # Save the time and solution self.prevt = intg.tcurr self.prevex = currex if dowrite: comm, rank, root = get_comm_rank_root() if self.mode == 'windowed': accex = self.accex tstart = self.tout_last else: for a, c in zip(self.accex, self.caccex): c += a accex = self.caccex tstart = self.tstart_actual # Normalise the accumulated expressions tavg = [a / (intg.tcurr - tstart) for a in accex] # Evaluate any functional expressions if self.fexprs: funex = self._eval_fun_exprs(intg, tavg) tavg = [np.hstack([a, f]) for a, f in zip(tavg, funex)] # Form the output records to be written to disk data = dict(self._ele_region_data) for (idx, etype, rgn), d in zip(self._ele_regions, tavg): data[etype] = d.astype(self.fpdtype) # If we are the root rank then prepare the metadata if rank == root: stats = Inifile() stats.set('data', 'prefix', 'tavg') stats.set('data', 'fields', ','.join(self.outfields)) stats.set('tavg', 'tstart', tstart) stats.set('tavg', 'tend', intg.tcurr) intg.collect_stats(stats) metadata = dict(intg.cfgmeta, stats=stats.tostr(), mesh_uuid=intg.mesh_uuid) else: metadata = None # Write to disk solnfname = self._writer.write(data, intg.tcurr, metadata) # If a post-action has been registered then invoke it self._invoke_postaction(intg=intg, mesh=intg.system.mesh.fname, soln=solnfname, t=intg.tcurr) # Reset the accumulators for a in self.accex: a.fill(0) self.tout_last = intg.tcurr
def __init__(self, backend, systemcls, rallocs, mesh, initsoln, cfg, tcoeffs, dt): self.backend = backend sect = 'solver-time-integrator' mgsect = 'solver-dual-time-integrator-multip' # Get the solver order and set the initial multigrid level self._order = self.level = order = cfg.getint('solver', 'order') # Get the multigrid cycle self.cycle, self.csteps = zip(*cfg.getliteral(mgsect, 'cycle')) self.levels = sorted(set(self.cycle), reverse=True) if max(self.cycle) > self._order: raise ValueError('The multigrid level orders cannot exceed ' 'the solution order') if any(abs(i - j) > 1 for i, j in zip(self.cycle, self.cycle[1:])): raise ValueError('The orders of consecutive multigrid levels can ' 'only change by one') if self.cycle[0] != self._order or self.cycle[-1] != self._order: raise ValueError('The multigrid cycle needs to start end with the ' 'highest (solution) order ') # Initialise the number of cycles self.npmgcycles = 0 # Multigrid pseudo-time steps dtau = cfg.getfloat(sect, 'pseudo-dt') self.dtauf = cfg.getfloat(mgsect, 'pseudo-dt-fact', 1.0) self._maxniters = cfg.getint(sect, 'pseudo-niters-max', 0) self._minniters = cfg.getint(sect, 'pseudo-niters-min', 0) # Get the multigrid pseudostepper and pseudocontroller classes pn = cfg.get(sect, 'pseudo-scheme') cn = cfg.get(sect, 'pseudo-controller') cc = subclass_where(BaseDualPseudoController, pseudo_controller_name=cn) cc_none = subclass_where(BaseDualPseudoController, pseudo_controller_name='none') # Construct a pseudo-integrator for each level from pyfr.integrators.dual.pseudo import get_pseudo_stepper_cls self.pintgs = {} for l in self.levels: pc = get_pseudo_stepper_cls(pn, l) if l == order: bases = [cc, pc] mcfg = cfg else: bases = [cc_none, pc] mcfg = Inifile(cfg.tostr()) mcfg.set('solver', 'order', l) mcfg.set(sect, 'pseudo-dt', dtau * self.dtauf**(order - l)) for s in cfg.sections(): m = re.match(f'solver-(.*)-mg-p{l}$', s) if m: mcfg.rename_section(s, f'solver-{m.group(1)}') # A class that bypasses pseudo-controller methods within a cycle class lpsint(*bases): name = 'MultiPPseudoIntegrator' + str(l) aux_nregs = 2 if l != self._order else 0 @property def _aux_regidx(iself): if iself.aux_nregs != 0: return iself._regidx[-2:] @property def ntotiters(iself): return self.npmgcycles def convmon(iself, *args, **kwargs): pass def finalise_pseudo_advance(iself, *args, **kwargs): pass def _rhs_with_dts(iself, t, uin, fout): # Compute -∇·f iself.system.rhs(t, uin, fout) # Coefficients for the physical stepper svals = [sc / iself._dt for sc in iself._stepper_coeffs] # Physical stepper source addition -∇·f - dQ/dt axnpby = iself._get_axnpby_kerns(len(svals) + 1, subdims=iself._subdims) iself._prepare_reg_banks(fout, iself._idxcurr, *iself._stepper_regidx) iself._queue.enqueue_and_run(axnpby, 1, *svals) # Multigrid r addition if iself._aux_regidx: axnpby = iself._get_axnpby_kerns(2) iself._prepare_reg_banks(fout, iself._aux_regidx[0]) iself._queue.enqueue_and_run(axnpby, 1, -1) self.pintgs[l] = lpsint(backend, systemcls, rallocs, mesh, initsoln, mcfg, tcoeffs, dt) # Get the highest p system from plugins self.system = self.pintgs[self._order].system # Get the convergence monitoring method self.mg_convmon = cc.convmon # Initialise the restriction and prolongation matrices self._init_proj_mats() # Delete remaining elements maps from multigrid systems for l in self.levels[1:]: del self.pintgs[l].system.ele_map
def __init__(self, backend, systemcls, rallocs, mesh, initsoln, cfg, stp_nregs, stg_nregs, dt): self.backend = backend sect = 'solver-time-integrator' mgsect = 'solver-dual-time-integrator-multip' # Get the solver order and set the initial multigrid level self._order = self.level = order = cfg.getint('solver', 'order') # Get the multigrid cycle self.cycle, self.csteps = zip(*cfg.getliteral(mgsect, 'cycle')) self.levels = sorted(set(self.cycle), reverse=True) if max(self.cycle) > self._order: raise ValueError('The multigrid level orders cannot exceed ' 'the solution order') if any(abs(i - j) > 1 for i, j in zip(self.cycle, self.cycle[1:])): raise ValueError('The orders of consecutive multigrid levels can ' 'only change by one') if self.cycle[0] != self._order or self.cycle[-1] != self._order: raise ValueError('The multigrid cycle needs to start end with the ' 'highest (solution) order ') # Initialise the number of cycles self.npmgcycles = 0 # Multigrid pseudo-time steps dtau = cfg.getfloat(sect, 'pseudo-dt') self.dtauf = cfg.getfloat(mgsect, 'pseudo-dt-fact', 1.0) self._maxniters = cfg.getint(sect, 'pseudo-niters-max', 0) self._minniters = cfg.getint(sect, 'pseudo-niters-min', 0) # Get the multigrid pseudostepper and pseudocontroller classes pn = cfg.get(sect, 'pseudo-scheme') cn = cfg.get(sect, 'pseudo-controller') cc = subclass_where(BaseDualPseudoController, pseudo_controller_name=cn) cc_none = subclass_where(BaseDualPseudoController, pseudo_controller_name='none') # Construct a pseudo-integrator for each level from pyfr.integrators.dual.pseudo import get_pseudo_stepper_cls self.pintgs = {} for l in self.levels: pc = get_pseudo_stepper_cls(pn, l) if l == order: bases = [cc, pc] mcfg = cfg else: bases = [cc_none, pc] mcfg = Inifile(cfg.tostr()) mcfg.set('solver', 'order', l) mcfg.set(sect, 'pseudo-dt', dtau * self.dtauf**(order - l)) for s in cfg.sections(): if (m := re.match(f'solver-(.*)-mg-p{l}$', s)): mcfg.rename_section(s, f'solver-{m.group(1)}') # A class that bypasses pseudo-controller methods within a cycle class lpsint(*bases): name = 'MultiPPseudoIntegrator' + str(l) aux_nregs = 2 if l != self._order else 0 stepper_nregs = stp_nregs if l == self._order else 0 stage_nregs = stg_nregs if l == self._order else 0 @property def _aux_regidx(iself): if iself.aux_nregs != 0: return iself._regidx[-2:] @property def ntotiters(iself): return self.npmgcycles def convmon(iself, *args, **kwargs): pass def _rhs_with_dts(iself, t, uin, fout, mg_add=True): # Compute -∇·f iself.system.rhs(t, uin, fout) if iself.stage_nregs > 1: iself._add(0, self._stage_regidx[iself.currstg], 1, fout) # Registers vals = iself.stepper_coeffs[:2] + [1] regs = [fout, iself._idxcurr, iself._source_regidx] # Physical stepper source addition -∇·f - dQ/dt iself._addv(vals, regs, subdims=iself._subdims) # Multigrid r addition if mg_add and iself._aux_regidx: iself._add(1, fout, -1, iself._aux_regidx[0]) self.pintgs[l] = lpsint(backend, systemcls, rallocs, mesh, initsoln, mcfg, stp_nregs, stg_nregs, dt)
def __call__(self, intg): # If we are not supposed to be averaging yet then return if intg.tcurr < self.tstart: return # If necessary, run the start-up routines if not self._started: self._init_accumex(intg) self._started = True # See if we are due to write and/or accumulate this step dowrite = intg.tcurr - self.tout_last >= self.dtout - self.tol doaccum = intg.nacptsteps % self.nsteps == 0 if dowrite or doaccum: # Evaluate the time averaging expressions currex = self._eval_acc_exprs(intg) # Accumulate them; always do this even when just writing for a, p, c in zip(self.accex, self.prevex, currex): a += 0.5 * (intg.tcurr - self.prevt) * (p + c) # Save the time and solution self.prevt = intg.tcurr self.prevex = currex if dowrite: if self.mode == 'windowed': accex = self.accex tstart = self.tout_last else: for a, c in zip(self.accex, self.caccex): c += a accex = self.caccex tstart = self.tstart_actual # Normalise the accumulated expressions data = [a / (intg.tcurr - tstart) for a in accex] # Evaluate any functional expressions if self.fexprs: funex = self._eval_fun_exprs(intg, data) data = [np.hstack([a, f]) for a, f in zip(data, funex)] # Prepare the stats record stats = Inifile() stats.set('data', 'prefix', 'tavg') stats.set('data', 'fields', ','.join(self.outfields)) stats.set('tavg', 'tstart', tstart) stats.set('tavg', 'tend', intg.tcurr) intg.collect_stats(stats) # Prepare the metadata metadata = dict(intg.cfgmeta, stats=stats.tostr(), mesh_uuid=intg.mesh_uuid) # Add in any required region data and write to disk data = self._add_region_data(data) solnfname = self._writer.write(data, metadata, intg.tcurr) # If a post-action has been registered then invoke it self._invoke_postaction(intg=intg, mesh=intg.system.mesh.fname, soln=solnfname, t=intg.tcurr) # Reset the accumulators for a in self.accex: a.fill(0) self.tout_last = intg.tcurr