def __call__(self, y, t, dt, rhs): try: self.residual except AttributeError: self.residual = 0 * rhs(t, y) from hedge.tools import count_dofs self.dof_count = count_dofs(self.residual) self.linear_combiner = self.vector_primitive_factory\ .make_linear_combiner(self.dtype, self.scalar_dtype, y, arg_count=2) lc = self.linear_combiner for a, b, c in self.coeffs: this_rhs = rhs(t + c * dt, y) sub_timer = self.timer.start_sub_timer() self.residual = lc((a, self.residual), (dt, this_rhs)) del this_rhs y = lc((1, y), (b, self.residual)) sub_timer.stop().submit() # 5 is the number of flops above, *NOT* the number of stages, # which is already captured in len(self.coeffs) self.flop_counter.add(len(self.coeffs) * self.dof_count * 5) return y
def adapt_step_size(t, dt, start_y, high_order_end_y, low_order_end_y, stepper, lc2, norm): normalization = stepper.atol + stepper.rtol * max(norm(low_order_end_y), norm(start_y)) error = lc2((1 / normalization, high_order_end_y), (-1 / normalization, low_order_end_y)) from hedge.tools import count_dofs rel_err = norm(error) / count_dofs(error)**0.5 if rel_err == 0: rel_err = 1e-14 if rel_err > 1 or numpy.isnan(rel_err): # reject step if not numpy.isnan(rel_err): dt = max(0.9 * dt * rel_err**(-1 / stepper.low_order), stepper.min_dt_shrinkage * dt) else: dt = stepper.min_dt_shrinkage * dt if t + dt == t: from hedge.timestep import TimeStepUnderflow raise TimeStepUnderflow() return False, dt, rel_err else: # accept step next_dt = min(0.9 * dt * rel_err**(-1 / stepper.high_order), stepper.max_dt_growth * dt) return True, next_dt, rel_err
def __call__(self, y, t, dt, rhs): try: self.residual except AttributeError: self.residual = 0*rhs(t, y) from hedge.tools import count_dofs self.dof_count = count_dofs(self.residual) self.linear_combiner = self.vector_primitive_factory\ .make_linear_combiner(self.dtype, self.scalar_dtype, y, arg_count=2) lc = self.linear_combiner for a, b, c in self.coeffs: this_rhs = rhs(t + c*dt, y) sub_timer = self.timer.start_sub_timer() self.residual = lc((a, self.residual), (dt, this_rhs)) del this_rhs y = lc((1, y), (b, self.residual)) sub_timer.stop().submit() # 5 is the number of flops above, *NOT* the number of stages, # which is already captured in len(self.coeffs) self.flop_counter.add(len(self.coeffs)*self.dof_count*5) return y
def __call__(self, y, t, dt, rhs): if len(self.f_history) == 0: # insert IC self.f_history.append(rhs(t, y)) from hedge.tools import count_dofs self.dof_count = count_dofs(self.f_history[0]) if len(self.f_history) < len(self.coefficients): ynew = self.startup_stepper(y, t, dt, rhs) if len(self.f_history) == len(self.coefficients) - 1: # here's some memory we won't need any more del self.startup_stepper else: from operator import add sub_timer = self.timer.start_sub_timer() assert len(self.coefficients) == len(self.f_history) ynew = y + dt * reduce(add, (coeff * f for coeff, f in zip(self.coefficients, self.f_history))) self.f_history.pop() sub_timer.stop().submit() self.flop_counter.add((2+2*len(self.coefficients)-1)*self.dof_count) self.f_history.insert(0, rhs(t+dt, ynew)) return ynew
def __call__(self, y, t, dt, rhs): if len(self.f_history) == 0: # insert IC self.f_history.append(rhs(t, y)) from hedge.tools import count_dofs self.dof_count = count_dofs(self.f_history[0]) if len(self.f_history) < len(self.coefficients): ynew = self.startup_stepper(y, t, dt, rhs) if len(self.f_history) == len(self.coefficients) - 1: # here's some memory we won't need any more del self.startup_stepper else: from operator import add sub_timer = self.timer.start_sub_timer() assert len(self.coefficients) == len(self.f_history) ynew = y + dt * reduce( add, (coeff * f for coeff, f in zip(self.coefficients, self.f_history))) self.f_history.pop() sub_timer.stop().submit() self.flop_counter.add( (2 + 2 * len(self.coefficients) - 1) * self.dof_count) self.f_history.insert(0, rhs(t + dt, ynew)) return ynew
def get_rhs(i): try: return rhss[i] except KeyError: result = rhs(t + time_fractions[i] * dt, row_values[i]) rhss[i] = result try: self.dof_count except AttributeError: from hedge.tools import count_dofs self.dof_count = count_dofs(result) return result
def get_rhs(i): try: return rhss[i] except KeyError: result = rhs(t + time_fractions[i]*dt, row_values[i]) rhss[i] = result try: self.dof_count except AttributeError: from hedge.tools import count_dofs self.dof_count = count_dofs(result) return result
def adapt_step_size(t, dt, start_y, high_order_end_y, low_order_end_y, stepper, lc2, norm): normalization = stepper.atol + stepper.rtol*max( norm(low_order_end_y), norm(start_y)) error = lc2( (1/normalization, high_order_end_y), (-1/normalization, low_order_end_y) ) from hedge.tools import count_dofs rel_err = norm(error)/count_dofs(error)**0.5 if rel_err == 0: rel_err = 1e-14 if rel_err > 1 or numpy.isnan(rel_err): # reject step last_dt = dt if not numpy.isnan(rel_err): dt = max( 0.9 * dt * rel_err**(-1/stepper.low_order), stepper.min_dt_shrinkage * dt) else: dt = stepper.min_dt_shrinkage*dt if t + dt == t: from hedge.timestep import TimeStepUnderflow raise TimeStepUnderflow() return False, dt, rel_err else: # accept step next_dt = min( 0.9 * dt * rel_err**(-1/stepper.high_order), stepper.max_dt_growth*dt) return True, next_dt, rel_err
def __call__(self, y, t, dt, rhs_expl, rhs_impl, reject_hook=None): r""" :arg rhs_impl: for a signature of (t, y0, alpha), returns a value of *k* satisfying .. math:: k = f(t, y_0 + \alpha*k) where *f* is the right-hand side function. .. note:: For a linear *f*, the relationship above may be rewritten as .. math:: (Id-\alpha A)k = A y_0. """ from hedge.tools import count_dofs # {{{ preparation, linear combiners try: self.last_rhs_expl except AttributeError: self.last_rhs_expl = rhs_expl(t, y) self.last_rhs_impl = rhs_impl(t, y, 0) self.dof_count = count_dofs(self.last_rhs_expl) if self.adaptive: self.norm = self.vector_primitive_factory.make_maximum_norm(self.last_rhs) else: self.norm = None # }}} flop_count = [0] while True: explicit_rhss = [] implicit_rhss = [] # {{{ stage loop for c, coeffs_expl, coeffs_impl in zip(self.c, self.a_explicit, self.a_implicit): if len(coeffs_expl) == 0: assert c == 0 assert len(coeffs_impl) == 0 this_rhs_expl = self.last_rhs_expl this_rhs_impl = self.last_rhs_impl else: sub_timer = self.timer.start_sub_timer() assert len(coeffs_expl) == len(explicit_rhss) assert len(coeffs_impl) == len(implicit_rhss) args = [(1, y)] + [ (dt * coeff, erhs) for coeff, erhs in zip(coeffs_expl + coeffs_impl, explicit_rhss + implicit_rhss) if coeff ] flop_count[0] += len(args) * 2 - 1 sub_y = self.get_linear_combiner(len(args), self.last_rhs_expl)(*args) sub_timer.stop().submit() this_rhs_expl = rhs_expl(t + c * dt, sub_y) this_rhs_impl = rhs_impl(t + c * dt, sub_y, self.gamma * dt) explicit_rhss.append(this_rhs_expl) implicit_rhss.append(this_rhs_impl) # }}} def finish_solution(coeffs): assert len(coeffs) == len(explicit_rhss) == len(implicit_rhss) args = [(1, y)] + [ (dt * coeff, erhs) for coeff, erhs in zip(coeffs + coeffs, explicit_rhss + implicit_rhss) if coeff ] flop_count[0] += len(args) * 2 - 1 return self.get_linear_combiner(len(args), self.last_rhs_expl)(*args) if not self.adaptive: if self.use_high_order: y = finish_solution(self.high_order_coeffs) else: y = finish_solution(self.low_order_coeffs) self.last_rhs_expl = this_rhs_expl self.last_rhs_impl = this_rhs_impl self.flop_counter.add(self.dof_count * flop_count[0]) return y else: # {{{ step size adaptation high_order_end_y = finish_solution(self.high_order_coeffs) low_order_end_y = finish_solution(self.low_order_coeffs) flop_count[0] += 3 + 1 # one two-lincomb, one norm from hedge.timestep.runge_kutta import adapt_step_size accept_step, next_dt, rel_err = adapt_step_size( t, dt, y, high_order_end_y, low_order_end_y, self, self.get_linear_combiner(2, self.last_rhs), self.norm, ) if not accept_step: if reject_hook: y = reject_hook(dt, rel_err, t, y) dt = next_dt # ... and go back to top of loop else: # finish up self.last_rhs_expl = this_rhs_expl self.last_rhs_impl = this_rhs_impl self.flop_counter.add(self.dof_count * flop_count[0]) return high_order_end_y, t + dt, dt, next_dt
def __call__(self, y, t, dt, rhs, reject_hook=None): from hedge.tools import count_dofs # {{{ preparation try: self.last_rhs except AttributeError: self.last_rhs = rhs(t, y) self.dof_count = count_dofs(self.last_rhs) if self.adaptive: self.norm = self.vector_primitive_factory \ .make_maximum_norm(self.last_rhs) else: self.norm = None # }}} flop_count = [0] while True: rhss = [] # {{{ stage loop for i, (c, coeffs) in enumerate(self.butcher_tableau): if len(coeffs) == 0: assert c == 0 this_rhs = self.last_rhs else: sub_timer = self.timer.start_sub_timer() args = [(1, y)] + [(dt * coeff, rhss[j]) for j, coeff in enumerate(coeffs) if coeff] flop_count[0] += len(args) * 2 - 1 sub_y = self.limiter( self.get_linear_combiner(len(args), self.last_rhs)(*args)) sub_timer.stop().submit() this_rhs = rhs(t + c * dt, sub_y) rhss.append(this_rhs) # }}} def finish_solution(coeffs): args = [(1, y)] + [(dt * coeff, rhss[i]) for i, coeff in enumerate(coeffs) if coeff] flop_count[0] += len(args) * 2 - 1 return self.get_linear_combiner(len(args), self.last_rhs)(*args) if not self.adaptive: if self.use_high_order: y = self.limiter(finish_solution(self.high_order_coeffs)) else: y = self.limiter(finish_solution(self.low_order_coeffs)) self.last_rhs = this_rhs self.flop_counter.add(self.dof_count * flop_count[0]) return y else: # {{{ step size adaptation high_order_end_y = finish_solution(self.high_order_coeffs) low_order_end_y = finish_solution(self.low_order_coeffs) flop_count[0] += 3 + 1 # one two-lincomb, one norm # Perform error estimation based on un-limited solutions. accept_step, next_dt, rel_err = adapt_step_size( t, dt, y, high_order_end_y, low_order_end_y, self, self.get_linear_combiner(2, high_order_end_y), self.norm) if not accept_step: if reject_hook: y = reject_hook(dt, rel_err, t, y) dt = next_dt # ... and go back to top of loop else: # finish up self.last_rhs = this_rhs self.flop_counter.add(self.dof_count * flop_count[0]) return self.limiter(high_order_end_y), t + dt, dt, next_dt
def __call__(self, y, t, dt, rhs, reject_hook=None): from hedge.tools import count_dofs # {{{ preparation try: self.last_rhs except AttributeError: self.last_rhs = rhs(t, y) self.dof_count = count_dofs(self.last_rhs) if self.adaptive: self.norm = self.vector_primitive_factory \ .make_maximum_norm(self.last_rhs) else: self.norm = None # }}} flop_count = [0] while True: rhss = [] # {{{ stage loop for i, (c, coeffs) in enumerate(self.butcher_tableau): if len(coeffs) == 0: assert c == 0 this_rhs = self.last_rhs else: sub_timer = self.timer.start_sub_timer() args = [(1, y)] + [ (dt*coeff, rhss[j]) for j, coeff in enumerate(coeffs) if coeff] flop_count[0] += len(args)*2 - 1 sub_y = self.limiter(self.get_linear_combiner( len(args), self.last_rhs)(*args)) sub_timer.stop().submit() this_rhs = rhs(t + c*dt, sub_y) rhss.append(this_rhs) # }}} def finish_solution(coeffs): args = [(1, y)] + [ (dt*coeff, rhss[i]) for i, coeff in enumerate(coeffs) if coeff] flop_count[0] += len(args)*2 - 1 return self.get_linear_combiner( len(args), self.last_rhs)(*args) if not self.adaptive: if self.use_high_order: y = self.limiter(finish_solution(self.high_order_coeffs)) else: y = self.limiter(finish_solution(self.low_order_coeffs)) self.last_rhs = this_rhs self.flop_counter.add(self.dof_count*flop_count[0]) return y else: # {{{ step size adaptation high_order_end_y = finish_solution(self.high_order_coeffs) low_order_end_y = finish_solution(self.low_order_coeffs) flop_count[0] += 3+1 # one two-lincomb, one norm # Perform error estimation based on un-limited solutions. accept_step, next_dt, rel_err = adapt_step_size( t, dt, y, high_order_end_y, low_order_end_y, self, self.get_linear_combiner(2, high_order_end_y), self.norm) if not accept_step: if reject_hook: y = reject_hook(dt, rel_err, t, y) dt = next_dt # ... and go back to top of loop else: # finish up self.last_rhs = this_rhs self.flop_counter.add(self.dof_count*flop_count[0]) return self.limiter(high_order_end_y), t+dt, dt, next_dt
def __call__(self, y, t, dt, rhs_expl, rhs_impl, reject_hook=None): """ :arg rhs_impl: for a signature of (t, y0, alpha), returns a value of *k* satisfying .. math:: k = f(t, y_0 + \alpha*k) where *f* is the right-hand side function. .. note:: For a linear *f*, the relationship above may be rewritten as .. math:: (Id-\alpha A)k = A y_0. """ from hedge.tools import count_dofs # {{{ preparation, linear combiners self.first_rhs_expl = rhs_expl(t, y) self.first_rhs_impl = rhs_impl(t, y, 0.) try: self.norm except AttributeError: self.dof_count = count_dofs(self.first_rhs_expl) if self.adaptive: self.norm = self.vector_primitive_factory \ .make_maximum_norm(self.first_rhs_expl) else: self.norm = None # }}} flop_count = [0] while True: explicit_rhss = [] implicit_rhss = [] # {{{ stage loop for c, coeffs_expl, coeffs_impl, in zip(self.c, self.a_explicit, self.a_implicit): if len(coeffs_expl) == 0: assert c == 0 assert len(coeffs_impl) == 0 this_rhs_expl = self.first_rhs_expl this_rhs_impl = self.first_rhs_impl else: sub_timer = self.timer.start_sub_timer() assert len(coeffs_expl) == len(explicit_rhss) assert len(coeffs_impl) == len(implicit_rhss) args = [(1, y)] + [ (dt * coeff, erhs) for coeff, erhs in zip(coeffs_expl + coeffs_impl, explicit_rhss + implicit_rhss) if coeff ] flop_count[0] += len(args) * 2 - 1 sub_y = self.get_linear_combiner(len(args), this_rhs_expl)(*args) this_rhs_impl = rhs_impl(t + c * dt, sub_y, self.gamma * dt) args = [(1, sub_y)] + [(self.gamma * dt, this_rhs_impl)] flop_count[0] += 2 sub_y = self.get_linear_combiner(len(args), this_rhs_expl)(*args) this_rhs_expl = rhs_expl(t + c * dt, sub_y) sub_timer.stop().submit() explicit_rhss.append(this_rhs_expl) implicit_rhss.append(this_rhs_impl) # }}} def finish_solution(coeffs): assert (len(coeffs) == len(explicit_rhss) == len(implicit_rhss)) args = [(1, y)] + [ (dt * coeff, erhs) for coeff, erhs in zip(coeffs + coeffs, explicit_rhss + implicit_rhss) if coeff ] flop_count[0] += len(args) * 2 - 1 return self.get_linear_combiner(len(args), this_rhs_expl)(*args) if not self.adaptive: if self.use_high_order: y = finish_solution(self.high_order_coeffs) else: y = finish_solution(self.low_order_coeffs) self.flop_counter.add(self.dof_count * flop_count[0]) return y else: # {{{ step size adaptation high_order_end_y = finish_solution(self.high_order_coeffs) low_order_end_y = finish_solution(self.low_order_coeffs) flop_count[0] += 3 + 1 # one two-lincomb, one norm from hedge.timestep.runge_kutta import adapt_step_size accept_step, next_dt, rel_err = adapt_step_size( t, dt, y, high_order_end_y, low_order_end_y, self, self.get_linear_combiner(2, this_rhs_expl), self.norm) if not accept_step: if reject_hook: y = reject_hook(dt, rel_err, t, y) dt = next_dt # ... and go back to top of loop else: # finish up self.flop_counter.add(self.dof_count * flop_count[0]) return high_order_end_y, t + dt, dt, next_dt