Exemple #1
0
    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
Exemple #2
0
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
Exemple #3
0
    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
Exemple #4
0
    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
Exemple #5
0
    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
Exemple #6
0
        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
Exemple #7
0
        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
Exemple #8
0
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
Exemple #9
0
    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
Exemple #10
0
    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
Exemple #11
0
    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
Exemple #12
0
    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