# -*- coding: utf-8 -*-
import numpy as np
from pyfr.integrators.dual.pseudo.base import BaseDualPseudoIntegrator
from pyfr.mpiutil import get_comm_rank_root, get_mpi
from pyfr.util import memoize
class BaseDualPseudoController(BaseDualPseudoIntegrator):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Stats on the most recent step
self.pseudostepinfo = []
@memoize
def _get_errest_kerns(self):
return self._get_kernels('errest', nargs=3, norm=self._pseudo_norm)
def _resid(self, dtau, x):
comm, rank, root = get_comm_rank_root()
# Get an errest kern to compute the square of the maximum residual
errest = self._get_errest_kerns()
# Prepare and run the kernel
self._prepare_reg_banks(x, x, x)
self._queue.enqueue_and_run(errest, dtau, 0.0)
# L2 norm
if self._pseudo_norm == 'l2':
# Reduce locally (element types) and globally (MPI ranks)
res = np.array([sum(ev) for ev in zip(*errest.retval)])
comm.Allreduce(get_mpi('in_place'), res, op=get_mpi('sum'))
# Normalise and return
return np.sqrt(res / self._gndofs)
# L^∞ norm
else:
# Reduce locally (element types) and globally (MPI ranks)
res = np.array([max(ev) for ev in zip(*errest.retval)])
comm.Allreduce(get_mpi('in_place'), res, op=get_mpi('max'))
# Normalise and return
return np.sqrt(res)
def _update_pseudostepinfo(self, niters, resid):
self.pseudostepinfo.append((self.ntotiters, niters, resid))
[docs]class DualNonePseudoController(BaseDualPseudoController):
pseudo_controller_name = 'none'
pseudo_controller_needs_lerrest = False
[docs] def convmon(self, i, minniters):
if i >= minniters - 1:
# Subtract the current solution from the previous solution
self._add(-1.0, self._idxprev, 1.0, self._idxcurr)
# Compute the normalised residual
resid = tuple(self._resid(self._dtau, self._idxprev))
self._update_pseudostepinfo(i + 1, resid)
return all(r <= t for r, t in zip(resid, self._pseudo_residtol))
else:
self._update_pseudostepinfo(i + 1, None)
return False
[docs] def pseudo_advance(self, tcurr):
self.tcurr = tcurr
for i in range(self.maxniters):
# Take the step
self._idxcurr, self._idxprev = self.step(self.tcurr)
# Convergence monitoring
if self.convmon(i, self.minniters):
break
# Update
self.finalise_pseudo_advance(self._idxcurr)
[docs]class DualPIPseudoController(BaseDualPseudoController):
pseudo_controller_name = 'local-pi'
pseudo_controller_needs_lerrest = True
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
sect = 'solver-time-integrator'
# Error norm
self._norm = self.cfg.get(sect, 'errest-norm', 'l2')
if self._norm not in {'l2', 'uniform'}:
raise ValueError('Invalid error norm')
tplargs = {'nvars': self.system.nvars}
# Error tolerance
tplargs['atol'] = self.cfg.getfloat(sect, 'atol')
# PI control values
sord = self.pseudo_stepper_order
tplargs['expa'] = self.cfg.getfloat(sect, 'pi-alpha', 0.7) / sord
tplargs['expb'] = self.cfg.getfloat(sect, 'pi-beta', 0.4) / sord
# Constants
tplargs['maxf'] = self.cfg.getfloat(sect, 'max-fact', 1.01)
tplargs['minf'] = self.cfg.getfloat(sect, 'min-fact', 0.98)
tplargs['saff'] = self.cfg.getfloat(sect, 'safety-fact', 0.8)
tplargs['dtau_maxf'] = self.cfg.getfloat(sect, 'pseudo-dt-max-mult',
3.0)
# Limits for the local pseudo-time-step size
tplargs['dtau_min'] = self._dtau
tplargs['dtau_max'] = tplargs['dtau_maxf'] * self._dtau
# Register a kernel to compute local error
self.backend.pointwise.register(
'pyfr.integrators.dual.pseudo.kernels.localerrest'
)
for ele, shape, dtaumat in zip(self.system.ele_map.values(),
self.system.ele_shapes, self.dtau_upts):
# Allocate storage for previous error
err_prev = self.backend.matrix(shape, np.ones(shape),
tags={'align'})
# Append the error kernels to the proxylist
self.pintgkernels['localerrest'].append(
self.backend.kernel(
'localerrest', tplargs=tplargs,
dims=[ele.nupts, ele.neles], err=ele.scal_upts_inb,
errprev=err_prev, dtau_upts=dtaumat
)
)
self.backend.commit()
[docs] def localerrest(self, errbank):
self.system.eles_scal_upts_inb.active = errbank
self._queue.enqueue_and_run(self.pintgkernels['localerrest'])
[docs] def convmon(self, i, minniters):
if i >= minniters - 1:
# Subtract the current solution from the previous solution
self._add(-1.0, self._idxprev, 1.0, self._idxcurr)
# Divide by 1/dtau
self.localdtau(self._idxprev, inv=1)
# Reduction
resid = tuple(self._resid(1.0, self._idxprev))
self._update_pseudostepinfo(i + 1, resid)
return all(r <= t for r, t in zip(resid, self._pseudo_residtol))
else:
self._update_pseudostepinfo(i + 1, None)
return False
[docs] def pseudo_advance(self, tcurr):
self.tcurr = tcurr
for i in range(self.maxniters):
# Take the step
self._idxcurr, self._idxprev, self._idxerr = self.step(self.tcurr)
self.localerrest(self._idxerr)
if self.convmon(i, self.minniters):
break
# Update
self.finalise_pseudo_advance(self._idxcurr)