Discontinuous Galerkin Library
#include "dg/algorithm.h"
Loading...
Searching...
No Matches
adaptive.h
Go to the documentation of this file.
1#pragma once
2
3#include "backend/memory.h"
4#include "ode.h"
5#include "runge_kutta.h"
6
7namespace dg
8{
9
12//MW: The reason these are global lambdas instead of functions is so that make_unique
13//can deduce the correct types when used on dg::AdaptiveTimeloop:
14//std::make_unique<dg::AdaptiveTimeloop<Vec>>( adapt, ode, dg::pid_control, dg::fast_l2norm, 1e-6, 1e-6);
22static auto l2norm = [] ( const auto& x){ return sqrt( dg::blas1::dot(x,x));};
23
33static auto fast_l2norm = []( const auto& x){ return sqrt( dg::blas1::reduce(
34 x, (double)0, dg::Sum(), dg::Square()));};
35
37static auto i_control = []( auto dt, auto eps, unsigned embedded_order, unsigned order)
38{
39 using value_type = std::decay_t<decltype(dt[0])>;
40 return dt[0]*pow( eps[0], -1./(value_type)embedded_order);
41};
43static auto pi_control = []( auto dt, auto eps, unsigned embedded_order, unsigned order)
44{
45 using value_type = std::decay_t<decltype(dt[0])>;
46 if( dt[1] == 0)
47 return i_control( dt, eps, embedded_order, order);
48 value_type m_k1 = -0.8, m_k2 = 0.31;
49 value_type factor = pow( eps[0], m_k1/(value_type)embedded_order)
50 * pow( eps[1], m_k2/(value_type)embedded_order);
51 return dt[0]*factor;
52};
71static auto pid_control = []( auto dt, auto eps, unsigned embedded_order, unsigned order)
72{
73 using value_type = std::decay_t<decltype(dt[0])>;
74 if( dt[1] == 0)
75 return i_control( dt, eps, embedded_order, order);
76 if( dt[2] == 0)
77 return pi_control( dt, eps, embedded_order, order);
78 value_type m_k1 = -0.58, m_k2 = 0.21, m_k3 = -0.1;
79 //value_type m_k1 = -0.37, m_k2 = 0.27, m_k3 = -0.1;
80 value_type factor = pow( eps[0], m_k1/(value_type)embedded_order)
81 * pow( eps[1], m_k2/(value_type)embedded_order)
82 * pow( eps[2], m_k3/(value_type)embedded_order);
83 return dt[0]*factor;
84};
85
87static auto ex_control = [](auto dt, auto eps, unsigned embedded_order, unsigned order)
88{
89 using value_type = std::decay_t<decltype(dt[0])>;
90 if( dt[1] == 0)
91 return i_control( dt, eps, embedded_order, order);
92 value_type m_k1 = -0.367, m_k2 = 0.268;
93 value_type factor = pow( eps[0], m_k1/(value_type)embedded_order)
94 * pow( eps[0]/eps[1], m_k2/(value_type)embedded_order);
95 return dt[0]*factor;
96};
98static auto im_control = []( auto dt, auto eps, unsigned embedded_order, unsigned order)
99{
100 using value_type = std::decay_t<decltype(dt[0])>;
101 if( dt[1] == 0)
102 return i_control( dt, eps, embedded_order, order);
103 value_type m_k1 = -0.98, m_k2 = -0.95;
104 value_type factor = pow( eps[0], m_k1/(value_type)embedded_order)
105 * pow( eps[0]/eps[1], m_k2/(value_type)embedded_order);
106 return dt[0]*dt[0]/dt[1]*factor;
107};
109static auto imex_control = []( auto dt, auto eps, unsigned embedded_order, unsigned order)
110{
111 using value_type = std::decay_t<decltype(dt[0])>;
112 value_type h1 = ex_control( dt, eps, embedded_order, order);
113 value_type h2 = im_control( dt, eps, embedded_order, order);
114 // find value closest to zero
115 return fabs( h1) < fabs( h2) ? h1 : h2;
116};
117
119
121namespace detail{
122template<class value_type>
123struct Tolerance
124{
125 // sqrt(size) is norm( 1)
126 Tolerance( value_type rtol, value_type atol, value_type size) :
127 m_rtol(rtol*sqrt(size)), m_atol( atol*sqrt(size)){}
129 void operator()( value_type u0, value_type& delta) const{
130 delta = delta/ ( m_rtol*fabs(u0) + m_atol);
131 }
132 private:
133 value_type m_rtol, m_atol;
134};
135} //namespace detail
137
159//%%%%%%%%%%%%%%%%%%%Adaptive%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
231template<class Stepper>
233{
234 using stepper_type = Stepper;
235 using container_type = typename Stepper::container_type;
236 using value_type = typename Stepper::value_type;
237 Adaptive() = default;
244 template<class ...StepperParams>
245 Adaptive(StepperParams&& ...ps): m_stepper(std::forward<StepperParams>(ps)...),
246 m_next(m_stepper.copyable()), m_delta(m_stepper.copyable())
247 {
248 dg::blas1::copy( 1., m_next);
249 m_size = dg::blas1::dot( m_next, 1.);
250 }
252 template<class ...Params>
253 void construct(Params&& ...ps)
254 {
255 //construct and swap
256 *this = Adaptive( std::forward<Params>(ps)...);
257 }
258
262 stepper_type& stepper() { return m_stepper;}
264 const stepper_type& stepper() const { return m_stepper;}
265
272 template< class ODE,
273 class ControlFunction = value_type (std::array<value_type,3>,
274 std::array< value_type,3>, unsigned , unsigned),
275 class ErrorNorm = value_type( const container_type&)>
276 void step( ODE&& ode,
277 value_type t0,
278 const container_type& u0,
279 value_type& t1,
280 container_type& u1,
281 value_type& dt,
282 ControlFunction control,
283 ErrorNorm norm,
284 value_type rtol,
285 value_type atol,
286 value_type reject_limit = 2
287 )
288 {
289 // prevent overwriting u0 in case stepper fails
290 m_stepper.step( std::forward<ODE>(ode), t0, u0, m_t_next, m_next, dt,
291 m_delta);
292 m_nsteps++;
293 dg::blas1::subroutine( detail::Tolerance<value_type>( rtol, atol,
294 m_size), u0, m_delta);
295 m_eps0 = norm( m_delta);
296 m_dt0 = dt;
297 if( m_eps0 > reject_limit || std::isnan( m_eps0) )
298 {
299 // if stepper fails, restart controller
300 dt = control( std::array<value_type,3>{m_dt0, 0, m_dt2},
301 std::array<value_type,3>{m_eps0, m_eps1, m_eps2},
302 m_stepper.embedded_order(),
303 m_stepper.order());
304 if( fabs( dt) > 0.9*fabs(m_dt0))
305 dt = 0.9*m_dt0;
306 //0.9*m_dt0 is a safety limit
307 //that prevents an increase of the timestep in case the stepper fails
308 m_failed = true; m_nfailed++;
309 dg::blas1::copy( u0, u1);
310 t1 = t0;
311 return;
312 }
313 if( m_eps0 < 1e-30) // small or zero
314 {
315 dt = 1e14*m_dt0; // a very large number
316 m_eps0 = 1e-30; // prevent storing zero
317 }
318 else
319 {
320 dt = control( std::array<value_type,3>{m_dt0, m_dt1, m_dt2},
321 std::array<value_type,3>{m_eps0, m_eps1, m_eps2},
322 m_stepper.embedded_order(),
323 m_stepper.order());
324 // a safety net
325 if( fabs(dt) > 100*fabs(m_dt0))
326 dt = 100*m_dt0;
327 }
328 m_eps2 = m_eps1;
329 m_eps1 = m_eps0;
330 m_dt2 = m_dt1;
331 m_dt1 = m_dt0;
332 dg::blas1::copy( m_next, u1);
333 t1 = m_t_next;
334 m_failed = false;
335 }
336
338 bool failed() const {
339 return m_failed;
340 }
342 const unsigned& nfailed() const {
343 return m_nfailed;
344 }
346 unsigned& nfailed() {
347 return m_nfailed;
348 }
350 const unsigned& nsteps() const {
351 return m_nsteps;
352 }
354 unsigned& nsteps() {
355 return m_nsteps;
356 }
357
364 const value_type& get_error( ) const{
365 return m_eps0;
366 }
367 private:
368 void reset_history(){
369 m_eps1 = m_eps2 = 1.;
370 m_dt1 = m_dt2 = 0.;
371 }
372 bool m_failed = false;
373 unsigned m_nfailed = 0;
374 unsigned m_nsteps = 0;
375 Stepper m_stepper;
376 container_type m_next, m_delta;
377 value_type m_size, m_eps0 = 1, m_eps1=1, m_eps2=1;
378 value_type m_t_next = 0;
379 value_type m_dt0 = 0., m_dt1 = 0., m_dt2 = 0.;
380};
467{
469 template<class T>
470 bool contains( T& t) const { return true;}
471};
472
475//
498template<class ContainerType>
499struct AdaptiveTimeloop : public aTimeloop<ContainerType>
500{
502 using container_type = ContainerType;
504 AdaptiveTimeloop( ) = default;
505
506
526 AdaptiveTimeloop( std::function<void (value_type, const ContainerType&,
527 value_type&, ContainerType&, value_type&)> step) :
528 m_step(step){
529 m_dt_current = 0.;
530 }
544 template<class Adaptive, class ODE, class ErrorNorm =
545 value_type( const container_type&), class
546 ControlFunction = value_type(std::array<value_type,3>,
547 std::array<value_type,3>, unsigned, unsigned)>
549 Adaptive&& adapt,
550 ODE&& ode,
551 ControlFunction control,
552 ErrorNorm norm,
553 value_type rtol,
554 value_type atol,
555 value_type reject_limit = 2)
556 {
557 // On the problem of capturing perfect forwarding in a lambda
558 //https://stackoverflow.com/questions/26831382/capturing-perfectly-forwarded-variable-in-lambda
559 //https://vittorioromeo.info/index/blog/capturing_perfectly_forwarded_objects_in_lambdas.html
560
561 m_step = [=, cap = std::tuple<Adaptive, ODE>(std::forward<Adaptive>(adapt),
562 std::forward<ODE>(ode)) ]( auto t0, const auto& y0, auto& t,
563 auto& y, auto& dt) mutable
564 {
565 std::get<0>(cap).step( std::get<1>(cap), t0, y0, t, y, dt, control, norm,
566 rtol, atol, reject_limit);
567 };
568 m_dt_current = 0.;
569 }
570
572 template<class ...Params>
573 void construct( Params&& ...ps)
574 {
575 //construct and swap
576 *this = AdaptiveTimeloop( std::forward<Params>( ps)...);
577 }
578
590 m_dt_current = dt;
591 }
592
617 template< class Domain>
619 value_type t0,
620 const ContainerType& u0,
621 value_type& t1,
622 ContainerType& u1,
623 value_type dt,
624 Domain&& domain,
625 value_type eps_root
626 );
627
628 virtual AdaptiveTimeloop* clone() const{return new
629 AdaptiveTimeloop(*this);}
630 private:
631 virtual void do_integrate(value_type& t0, const container_type& u0,
632 value_type t1, container_type& u1, enum to mode) const;
633 std::function<void( value_type, const ContainerType&, value_type&,
634 ContainerType&, value_type&)> m_step;
635 virtual value_type do_dt( ) const { return m_dt_current;}
636 mutable value_type m_dt_current ; // omg mutable exists !? write even if const
637};
638
640template< class ContainerType>
641void AdaptiveTimeloop<ContainerType>::do_integrate(
642 value_type& t_current,
643 const ContainerType& u0,
644 value_type t1,
645 ContainerType& u1,
646 enum to mode
647 )const
648{
649 value_type deltaT = t1-t_current;
650 bool forward = (deltaT > 0);
651
652 value_type& dt_current = m_dt_current;
653 if( dt_current == 0)
654 dt_current = forward ? 1e-6 : -1e-6; // a good a guess as any
655 if( (dt_current < 0 && forward) || ( dt_current > 0 && !forward) )
656 throw dg::Error( dg::Message(_ping_)<<"Error in AdaptiveTimeloop: Timestep has wrong sign! You cannot change direction mid-step: dt "<<dt_current);
657
658 blas1::copy( u0, u1 );
659 while( (forward && t_current < t1) || (!forward && t_current > t1))
660 {
661 if( dg::to::exact == mode
662 &&( (forward && t_current + dt_current > t1)
663 || (!forward && t_current + dt_current < t1) ) )
664 dt_current = t1-t_current;
665 if( dg::to::at_least == mode
666 &&( (forward && dt_current > deltaT)
667 || (!forward && dt_current < deltaT) ) )
668 dt_current = deltaT;
669 // Compute a step and error
670 try{
671 m_step( t_current, u1, t_current, u1, dt_current);
672 }catch ( dg::Error& e)
673 {
674 e.append( dg::Message(_ping_) << "Error in AdaptiveTimeloop::integrate");
675 throw;
676 }
677 if( !std::isfinite(dt_current) || fabs(dt_current) < 1e-9*fabs(deltaT))
678 {
679 value_type dt_current0 = dt_current;
680 dt_current = 0.;
681 throw dg::Error(dg::Message(_ping_)<<"Adaptive integrate failed to converge! dt = "<<std::scientific<<dt_current0);
682 }
683 }
684}
685
686template< class ContainerType>
687template< class Domain>
689 value_type t0,
690 const ContainerType& u0,
691 value_type& t1,
692 ContainerType& u1,
693 value_type dt,
694 Domain&& domain,
695 value_type eps_root
696 )
697{
698 value_type t_current = t0, dt_current = dt;
699 blas1::copy( u0, u1 );
700 ContainerType& current(u1);
701 if( t1 == t0)
702 return;
703 bool forward = (t1 - t0 > 0);
704 if( dt == 0)
705 dt_current = forward ? 1e-6 : -1e-6; // a good a guess as any
706
707 ContainerType last( u0);
708 while( (forward && t_current < t1) || (!forward && t_current > t1))
709 {
710 //remember last step
711 t0 = t_current;
712 dg::blas1::copy( current, last);
713 if( (forward && t_current+dt_current > t1) || (!forward && t_current +
714 dt_current < t1) )
715 dt_current = t1-t_current;
716 // Compute a step and error
717 try{
718 m_step( t_current, current, t_current, current, dt_current);
719 }catch ( dg::Error& e)
720 {
721 e.append( dg::Message(_ping_) << "Error in AdaptiveTimeloop::integrate");
722 throw;
723 }
724 if( !std::isfinite(dt_current) || fabs(dt_current) < 1e-9*fabs(t1-t0))
725 throw dg::Error(dg::Message(_ping_)<<"integrate_in_domain failed to converge! dt = "<<std::scientific<<dt_current);
726 if( !domain.contains( current) )
727 {
728 //start bisection between t0 and t1
729 t1 = t_current;
730
731 // t0 and last are inside
732 dg::blas1::copy( last, current);
733
734 int j_max = 50;
735 for(int j=0; j<j_max; j++)
736 {
737 if( fabs(t1-t0) < eps_root*fabs(t1) + eps_root)
738 {
739 return;
740 }
741 dt_current = (t1-t0)/2.;
742 t_current = t0; //always start integrate from inside
743 value_type failed = t_current;
744 // Naively we would assume that the timestepper always succeeds
745 // because dt_current is always smaller than the previous timestep
746 // However, there are cases when this is not true so we need to
747 // explicitly check!!
748 // t_current = t0, current = last (inside)
749 m_step( t_current, current, t_current, current, dt_current);
750 if( failed == t_current)
751 {
752 dt_current = (t1-t0)/4.;
753 break; // we need to get out of here
754 }
755
756 //stepper( t0, last, t_middle, u1, (t1-t0)/2.);
757 if( domain.contains( current) )
758 {
759 t0 = t_current;
760 dg::blas1::copy( current, last);
761 }
762 else
763 {
764 t1 = t_current;
765 dg::blas1::copy( last, current);
766 }
767 if( (j_max - 1) == j)
768 throw dg::Error( dg::Message(_ping_)<<"integrate_in_domain: too many steps in root finding!");
769 }
770 }
771 }
772}
774
776}//namespace dg
class intended for the use in throw statements
Definition exceptions.h:83
void append(const Message &message)
Appends a message verbatim to the what string.
Definition exceptions.h:101
small class holding a stringstream
Definition exceptions.h:29
#define _ping_
Definition exceptions.h:12
void copy(const ContainerTypeIn &source, ContainerTypeOut &target)
Definition blas1.h:243
OutputType reduce(const ContainerType &x, OutputType zero, BinaryOp binary_op, UnaryOp unary_op=UnaryOp())
Custom (transform) reduction
Definition blas1.h:215
void subroutine(Subroutine f, ContainerType &&x, ContainerTypes &&... xs)
; Customizable and generic blas1 function
Definition blas1.h:677
auto dot(const ContainerType1 &x, const ContainerType2 &y)
Binary reproducible Euclidean dot product between two vectors
Definition blas1.h:153
@ forward
forward derivative (cell to the right and current cell)
Definition enums.h:98
@ y
y direction
@ x
x direction
typename TensorTraits< std::decay_t< Vector > >::value_type get_value_type
Definition tensor_traits.h:45
static auto ex_control
Definition adaptive.h:87
static auto imex_control
h_{n+1} = |ex_control| < |im_control| ? ex_control : im_control
Definition adaptive.h:109
static auto pid_control
Definition adaptive.h:71
static auto l2norm
Compute using dg::blas1::dot.
Definition adaptive.h:22
to
Switch for the Timeloop integrate function.
Definition ode.h:17
static auto i_control
Definition adaptive.h:37
static auto pi_control
Definition adaptive.h:43
static auto fast_l2norm
Compute using naive summation.
Definition adaptive.h:33
static auto im_control
Definition adaptive.h:98
@ exact
match the ending exactly
@ at_least
integrate to the end or further
#define DG_DEVICE
Expands to __host__ __device__ if compiled with nvcc else is empty.
Definition dg_doc.h:378
This is the namespace for all functions and classes defined and used by the discontinuous Galerkin li...
An abstract ODE integrator.
Runge-Kutta explicit ODE-integrators.
Driver class for adaptive timestep ODE integration.
Definition adaptive.h:233
const stepper_type & stepper() const
Read access to internal stepper.
Definition adaptive.h:264
unsigned & nfailed()
Set total number of failed steps.
Definition adaptive.h:346
void step(ODE &&ode, value_type t0, const container_type &u0, value_type &t1, container_type &u1, value_type &dt, ControlFunction control, ErrorNorm norm, value_type rtol, value_type atol, value_type reject_limit=2)
Semi-implicit adaptive step.
Definition adaptive.h:276
stepper_type & stepper()
Allow write access to internal stepper.
Definition adaptive.h:262
const unsigned & nfailed() const
Get total number of failed steps.
Definition adaptive.h:342
Stepper stepper_type
Definition adaptive.h:234
Adaptive()=default
unsigned & nsteps()
Re-set total number of step calls.
Definition adaptive.h:354
void construct(Params &&...ps)
Perfect forward parameters to one of the constructors.
Definition adaptive.h:253
typename Stepper::container_type container_type
the type of the vector class in use by Stepper
Definition adaptive.h:235
typename Stepper::value_type value_type
the value type of the time variable defined by Stepper (float or double)
Definition adaptive.h:236
const unsigned & nsteps() const
Get total number of step calls.
Definition adaptive.h:350
const value_type & get_error() const
Get the latest error norm relative to solution vector.
Definition adaptive.h:364
bool failed() const
Return true if the last stepsize in step was rejected.
Definition adaptive.h:338
Adaptive(StepperParams &&...ps)
Allocate workspace and construct stepper.
Definition adaptive.h:245
Integrate using a while loop.
Definition adaptive.h:500
AdaptiveTimeloop()=default
no allocation
AdaptiveTimeloop(Adaptive &&adapt, ODE &&ode, ControlFunction control, ErrorNorm norm, value_type rtol, value_type atol, value_type reject_limit=2)
Bind the step function of a dg::Adaptive object.
Definition adaptive.h:548
virtual AdaptiveTimeloop * clone() const
Abstract copy method that returns a copy of *this on the heap.
Definition adaptive.h:628
dg::get_value_type< ContainerType > value_type
Definition adaptive.h:501
AdaptiveTimeloop(std::function< void(value_type, const ContainerType &, value_type &, ContainerType &, value_type &)> step)
Construct using a std::function.
Definition adaptive.h:526
void construct(Params &&...ps)
Perfect forward parameters to one of the constructors.
Definition adaptive.h:573
void set_dt(value_type dt)
Set initial time-guess in integrate function.
Definition adaptive.h:589
void integrate_in_domain(value_type t0, const ContainerType &u0, value_type &t1, ContainerType &u1, value_type dt, Domain &&domain, value_type eps_root)
Integrate a differential equation within an integration domain.
ContainerType container_type
Definition adaptive.h:502
The domain that contains all points.
Definition adaptive.h:467
bool contains(T &t) const
always true
Definition adaptive.h:470
Definition functors.h:75
Definition subroutines.h:73
Abstract timeloop independent of stepper and ODE.
Definition ode.h:60