foodie_test_oscillation.f90 Source File

Test FOODIE with the integration of Oscillation equations.

This File Depends On

sourcefile~~foodie_test_oscillation.f90~~EfferentGraph sourcefile~foodie_test_oscillation.f90 foodie_test_oscillation.f90 sourcefile~foodie.f90 foodie.f90 sourcefile~foodie.f90->sourcefile~foodie_test_oscillation.f90 sourcefile~foodie_test_integrand_oscillation.f90 foodie_test_integrand_oscillation.f90 sourcefile~foodie.f90->sourcefile~foodie_test_integrand_oscillation.f90 sourcefile~foodie_test_integrand_tester_object.f90 foodie_test_integrand_tester_object.f90 sourcefile~foodie.f90->sourcefile~foodie_test_integrand_tester_object.f90 sourcefile~foodie_test_integrand_oscillation.f90->sourcefile~foodie_test_oscillation.f90 sourcefile~foodie_integrator_adams_bashforth.f90 foodie_integrator_adams_bashforth.f90 sourcefile~foodie_integrator_adams_bashforth.f90->sourcefile~foodie.f90 sourcefile~foodie_integrator_adams_bashforth_moulton.f90 foodie_integrator_adams_bashforth_moulton.f90 sourcefile~foodie_integrator_adams_bashforth.f90->sourcefile~foodie_integrator_adams_bashforth_moulton.f90 sourcefile~foodie_integrator_multistage_object.f90 foodie_integrator_multistage_object.f90 sourcefile~foodie_integrator_multistage_object.f90->sourcefile~foodie.f90 sourcefile~foodie_integrator_runge_kutta_low_storage.f90 foodie_integrator_runge_kutta_low_storage.f90 sourcefile~foodie_integrator_multistage_object.f90->sourcefile~foodie_integrator_runge_kutta_low_storage.f90 sourcefile~foodie_integrator_euler_explicit.f90 foodie_integrator_euler_explicit.f90 sourcefile~foodie_integrator_multistage_object.f90->sourcefile~foodie_integrator_euler_explicit.f90 sourcefile~foodie_integrator_runge_kutta_lssp.f90 foodie_integrator_runge_kutta_lssp.f90 sourcefile~foodie_integrator_multistage_object.f90->sourcefile~foodie_integrator_runge_kutta_lssp.f90 sourcefile~foodie_integrator_runge_kutta_ssp.f90 foodie_integrator_runge_kutta_ssp.f90 sourcefile~foodie_integrator_multistage_object.f90->sourcefile~foodie_integrator_runge_kutta_ssp.f90 sourcefile~foodie_integrator_runge_kutta_embedded.f90 foodie_integrator_runge_kutta_embedded.f90 sourcefile~foodie_integrator_multistage_object.f90->sourcefile~foodie_integrator_runge_kutta_embedded.f90 sourcefile~foodie_integrator_lmm_ssp.f90 foodie_integrator_lmm_ssp.f90 sourcefile~foodie_integrator_lmm_ssp.f90->sourcefile~foodie.f90 sourcefile~foodie_integrator_adams_bashforth_moulton.f90->sourcefile~foodie.f90 sourcefile~foodie_integrator_lmm_ssp_vss.f90 foodie_integrator_lmm_ssp_vss.f90 sourcefile~foodie_integrator_lmm_ssp_vss.f90->sourcefile~foodie.f90 sourcefile~foodie_integrator_runge_kutta_low_storage.f90->sourcefile~foodie.f90 sourcefile~foodie_integrator_backward_differentiation_formula.f90 foodie_integrator_backward_differentiation_formula.f90 sourcefile~foodie_integrator_backward_differentiation_formula.f90->sourcefile~foodie.f90 sourcefile~foodie_integrator_multistage_multistep_object.f90 foodie_integrator_multistage_multistep_object.f90 sourcefile~foodie_integrator_multistage_multistep_object.f90->sourcefile~foodie.f90 sourcefile~foodie_integrator_ms_runge_kutta_ssp.f90 foodie_integrator_ms_runge_kutta_ssp.f90 sourcefile~foodie_integrator_multistage_multistep_object.f90->sourcefile~foodie_integrator_ms_runge_kutta_ssp.f90 sourcefile~foodie_error_codes.f90 foodie_error_codes.f90 sourcefile~foodie_error_codes.f90->sourcefile~foodie.f90 sourcefile~foodie_error_codes.f90->sourcefile~foodie_integrator_adams_bashforth.f90 sourcefile~foodie_error_codes.f90->sourcefile~foodie_integrator_lmm_ssp.f90 sourcefile~foodie_error_codes.f90->sourcefile~foodie_integrator_adams_bashforth_moulton.f90 sourcefile~foodie_error_codes.f90->sourcefile~foodie_integrator_lmm_ssp_vss.f90 sourcefile~foodie_error_codes.f90->sourcefile~foodie_integrator_runge_kutta_low_storage.f90 sourcefile~foodie_error_codes.f90->sourcefile~foodie_integrator_backward_differentiation_formula.f90 sourcefile~foodie_error_codes.f90->sourcefile~foodie_integrator_ms_runge_kutta_ssp.f90 sourcefile~foodie_error_codes.f90->sourcefile~foodie_integrator_euler_explicit.f90 sourcefile~foodie_error_codes.f90->sourcefile~foodie_integrator_runge_kutta_lssp.f90 sourcefile~foodie_error_codes.f90->sourcefile~foodie_integrator_runge_kutta_ssp.f90 sourcefile~foodie_integrator_adams_moulton.f90 foodie_integrator_adams_moulton.f90 sourcefile~foodie_error_codes.f90->sourcefile~foodie_integrator_adams_moulton.f90 sourcefile~foodie_integrator_leapfrog.f90 foodie_integrator_leapfrog.f90 sourcefile~foodie_error_codes.f90->sourcefile~foodie_integrator_leapfrog.f90 sourcefile~foodie_error_codes.f90->sourcefile~foodie_integrator_runge_kutta_embedded.f90 sourcefile~foodie_integrator_ms_runge_kutta_ssp.f90->sourcefile~foodie.f90 sourcefile~foodie_integrator_euler_explicit.f90->sourcefile~foodie.f90 sourcefile~foodie_integrator_runge_kutta_lssp.f90->sourcefile~foodie.f90 sourcefile~foodie_integrator_runge_kutta_ssp.f90->sourcefile~foodie.f90 sourcefile~foodie_integrator_adams_moulton.f90->sourcefile~foodie.f90 sourcefile~foodie_integrator_adams_moulton.f90->sourcefile~foodie_integrator_adams_bashforth_moulton.f90 sourcefile~foodie_integrator_leapfrog.f90->sourcefile~foodie.f90 sourcefile~foodie_integrand_object.f90 foodie_integrand_object.F90 sourcefile~foodie_integrand_object.f90->sourcefile~foodie.f90 sourcefile~foodie_integrand_object.f90->sourcefile~foodie_integrator_adams_bashforth.f90 sourcefile~foodie_integrand_object.f90->sourcefile~foodie_integrator_multistage_object.f90 sourcefile~foodie_integrand_object.f90->sourcefile~foodie_integrator_lmm_ssp.f90 sourcefile~foodie_integrand_object.f90->sourcefile~foodie_integrator_adams_bashforth_moulton.f90 sourcefile~foodie_integrand_object.f90->sourcefile~foodie_integrator_lmm_ssp_vss.f90 sourcefile~foodie_integrand_object.f90->sourcefile~foodie_integrator_runge_kutta_low_storage.f90 sourcefile~foodie_integrand_object.f90->sourcefile~foodie_integrator_backward_differentiation_formula.f90 sourcefile~foodie_integrand_object.f90->sourcefile~foodie_integrator_multistage_multistep_object.f90 sourcefile~foodie_integrand_object.f90->sourcefile~foodie_integrator_ms_runge_kutta_ssp.f90 sourcefile~foodie_integrand_object.f90->sourcefile~foodie_integrator_euler_explicit.f90 sourcefile~foodie_integrand_object.f90->sourcefile~foodie_integrator_runge_kutta_lssp.f90 sourcefile~foodie_integrand_object.f90->sourcefile~foodie_integrator_runge_kutta_ssp.f90 sourcefile~foodie_integrand_object.f90->sourcefile~foodie_integrator_adams_moulton.f90 sourcefile~foodie_integrand_object.f90->sourcefile~foodie_integrator_leapfrog.f90 sourcefile~foodie_integrator_multistep_object.f90 foodie_integrator_multistep_object.f90 sourcefile~foodie_integrand_object.f90->sourcefile~foodie_integrator_multistep_object.f90 sourcefile~foodie_integrand_object.f90->sourcefile~foodie_integrator_runge_kutta_embedded.f90 sourcefile~foodie_integrator_multistep_object.f90->sourcefile~foodie.f90 sourcefile~foodie_integrator_multistep_object.f90->sourcefile~foodie_integrator_adams_bashforth.f90 sourcefile~foodie_integrator_multistep_object.f90->sourcefile~foodie_integrator_lmm_ssp.f90 sourcefile~foodie_integrator_multistep_object.f90->sourcefile~foodie_integrator_adams_bashforth_moulton.f90 sourcefile~foodie_integrator_multistep_object.f90->sourcefile~foodie_integrator_lmm_ssp_vss.f90 sourcefile~foodie_integrator_multistep_object.f90->sourcefile~foodie_integrator_backward_differentiation_formula.f90 sourcefile~foodie_integrator_multistep_object.f90->sourcefile~foodie_integrator_adams_moulton.f90 sourcefile~foodie_integrator_multistep_object.f90->sourcefile~foodie_integrator_leapfrog.f90 sourcefile~foodie_integrator_object.f90 foodie_integrator_object.f90 sourcefile~foodie_integrator_object.f90->sourcefile~foodie.f90 sourcefile~foodie_integrator_object.f90->sourcefile~foodie_integrator_adams_bashforth.f90 sourcefile~foodie_integrator_object.f90->sourcefile~foodie_integrator_multistage_object.f90 sourcefile~foodie_integrator_object.f90->sourcefile~foodie_integrator_lmm_ssp.f90 sourcefile~foodie_integrator_object.f90->sourcefile~foodie_integrator_adams_bashforth_moulton.f90 sourcefile~foodie_integrator_object.f90->sourcefile~foodie_integrator_lmm_ssp_vss.f90 sourcefile~foodie_integrator_object.f90->sourcefile~foodie_integrator_runge_kutta_low_storage.f90 sourcefile~foodie_integrator_object.f90->sourcefile~foodie_integrator_backward_differentiation_formula.f90 sourcefile~foodie_integrator_object.f90->sourcefile~foodie_integrator_multistage_multistep_object.f90 sourcefile~foodie_integrator_object.f90->sourcefile~foodie_integrator_ms_runge_kutta_ssp.f90 sourcefile~foodie_integrator_object.f90->sourcefile~foodie_integrator_euler_explicit.f90 sourcefile~foodie_integrator_object.f90->sourcefile~foodie_integrator_runge_kutta_lssp.f90 sourcefile~foodie_integrator_object.f90->sourcefile~foodie_integrator_runge_kutta_ssp.f90 sourcefile~foodie_integrator_object.f90->sourcefile~foodie_integrator_adams_moulton.f90 sourcefile~foodie_integrator_object.f90->sourcefile~foodie_integrator_leapfrog.f90 sourcefile~foodie_integrator_object.f90->sourcefile~foodie_integrator_multistep_object.f90 sourcefile~foodie_integrator_object.f90->sourcefile~foodie_integrator_runge_kutta_embedded.f90 sourcefile~foodie_integrator_runge_kutta_embedded.f90->sourcefile~foodie.f90 sourcefile~foodie_test_integrand_tester_object.f90->sourcefile~foodie_test_integrand_oscillation.f90
Help


Source Code

!< Test FOODIE with the integration of Oscillation equations.

module foodie_test_oscillation_test
!< Oscillation test handler definition.

use, intrinsic :: iso_fortran_env, only : stderr=>error_unit
use flap, only : command_line_interface
use foodie, only : foodie_integrator_class_names,      &
                   foodie_integrator_factory,          &
                   foodie_integrator_schemes,          &
                   integrator_adams_bashforth,         &
                   integrator_adams_bashforth_moulton, &
                   integrator_adams_moulton,           &
                   integrator_back_df,                 &
                   integrator_euler_explicit,          &
                   integrator_leapfrog,                &
                   integrator_lmm_ssp,                 &
                   integrator_lmm_ssp_vss,             &
                   integrator_ms_runge_kutta_ssp,      &
                   integrator_object,                  &
                   integrator_runge_kutta_emd,         &
                   integrator_runge_kutta_ls,          &
                   integrator_runge_kutta_lssp,        &
                   integrator_runge_kutta_ssp,         &
                   is_available, is_class_available
use foodie_test_integrand_oscillation, only : integrand_oscillation
use penf, only : I_P, R_P, FR_P, str, strz

implicit none
private
public :: oscillation_test

type :: oscillation_test
   !< Class to handle oscillation test(s).
   !<
   !< Test is driven by the Command Line Interface (CLI) options.
   !<
   !< Test has only 1 public method `execute`: it executes test(s) accordingly to cli options.
   private
   type(command_line_interface) :: cli                     !< Command line interface handler.
   integer(I_P)                 :: error=0                 !< Error handler.
   character(99)                :: scheme=''               !< Scheme used.
   logical                      :: is_fast=.false.         !< Flag for activating fast schemes.
   integer(I_P)                 :: implicit_iterations=0   !< Number of iterations (implicit solvers).
   integer(I_P)                 :: stages=0                !< Number of stages.
   real(R_P), allocatable       :: Dt(:)                   !< Time step(s) exercised.
   real(R_P)                    :: frequency=0.0_R_P       !< Oscillation frequency.
   real(R_P)                    :: U0(1:2)=[0._R_P,0._R_P] !< Initial conditions.
   real(R_P)                    :: final_time=0.0_R_P      !< Final integration time.
   logical                      :: results=.false.         !< Flag for activating results saving.
   character(99)                :: output=''               !< Output files basename.
   logical                      :: exact_solution=.false.  !< Flag for activating exact solution saving.
   logical                      :: errors_analysis=.false. !< Flag for activating errors analysis.
   contains
      ! public methods
      procedure, pass(self) :: execute !< Execute selected test(s).
      ! private methods
      procedure, pass(self), private :: initialize !< Initialize test: set Command Line Interface, parse it and check its validity.
      procedure, pass(self), private :: test       !< Perform the test.
endtype oscillation_test

contains
   ! public methods
   subroutine execute(self)
   !< Execute test(s).
   class(oscillation_test), intent(inout) :: self                  !< Test.
   character(99), allocatable             :: integrator_schemes(:) !< Name of FOODIE integrator schemes.
   integer(I_P)                           :: s                     !< Counter.

   call self%initialize
   if (trim(adjustl(self%scheme))/='all') then
      if (is_class_available(scheme=self%scheme)) then
        integrator_schemes = foodie_integrator_schemes(class_name=self%scheme)
      elseif (is_available(scheme=self%scheme)) then
        integrator_schemes = [trim(adjustl(self%scheme))]
      endif
   else
     integrator_schemes = foodie_integrator_schemes()
   endif
   do s=1, size(integrator_schemes, dim=1)
     self%scheme = trim(integrator_schemes(s))
     call self%test(scheme=self%scheme)
   enddo
   endsubroutine execute

   ! private methods
   subroutine initialize(self)
   !< Initialize test: set Command Line Interface, parse it and check its validity.
   class(oscillation_test), intent(inout) :: self !< Test.

   call set_cli
   call parse_cli
   contains
      subroutine set_cli()
      !< Set Command Line Interface.

      associate(cli => self%cli)
         call cli%init(progname    = 'foodie_test_oscillation',                                           &
                       authors     = 'Fortran-FOSS-Programmers',                                          &
                       license     = 'GNU GPLv3',                                                         &
                       description = 'Test FOODIE library on Oscillation equations integration',          &
                       examples    = ["foodie_test_oscillation --scheme euler_explicit --save_results  ", &
                                      "foodie_test_oscillation --scheme all -r                         "])
         call cli%add(switch='--scheme', switch_ab='-s', help='integrator scheme used', required=.false., def='all', act='store')
         call cli%add(switch='--fast', help='activate fast solvers', required=.false., act='store_true', def='.false.')
         call cli%add(switch='--iterations', help='iterations number for implicit schemes', required=.false., act='store', def='5')
         call cli%add(switch='--stages', help='stages number', required=.false., def='2', act='store')
         call cli%add(switch='--time_step', switch_ab='-Dt', nargs='+', help='time step', required=.false., def='1e2', act='store')
         call cli%add(switch='--frequency', switch_ab='-f', help='frequency', required=.false., def='1e-4', act='store')
         call cli%add(switch='--U0', switch_ab='-U0', nargs='2', help='initial state', required=.false., def='0.0 1.0', act='store')
         call cli%add(switch='--t_final', switch_ab='-tf', help='final integration time', required=.false., def='1e5', act='store')
         call cli%add(switch='--save_results', switch_ab='-r',help='save result', required=.false., act='store_true', def='.false.')
         call cli%add(switch='--output', help='output file basename', required=.false., act='store', def='foodie_test_oscillation')
         call cli%add(switch='--exact_solution', help='save exact solutiion', required=.false., act='store_true', def='.false.')
         call cli%add(switch='--errors_analysis', help='peform errors analysis', required=.false., act='store_true', def='.false.')
      endassociate
      endsubroutine set_cli

      subroutine parse_cli()
      !< Parse Command Line Interface and check its validity.
      character(99), allocatable :: integrator_class_names(:) !< Name of FOODIE integrator classes.
      character(99), allocatable :: integrator_schemes(:)     !< Name of FOODIE integrator schemes.
      integer(I_P)               :: i                         !< Counter.

      call self%cli%parse(error=self%error)
      call self%cli%get(switch='-s', val=self%scheme, error=self%error) ; if (self%error/=0) stop
      call self%cli%get(switch='--fast', val=self%is_fast, error=self%error) ; if (self%error/=0) stop
      call self%cli%get(switch='--iterations', val=self%implicit_iterations, error=self%error) ; if (self%error/=0) stop
      call self%cli%get(switch='--stages', val=self%stages, error=self%error) ; if (self%error/=0) stop
      call self%cli%get_varying(switch='-Dt', val=self%Dt, error=self%error) ; if (self%error/=0) stop
      call self%cli%get(switch='-f', val=self%frequency, error=self%error) ; if (self%error/=0) stop
      call self%cli%get(switch='-U0', val=self%U0, error=self%error) ; if (self%error/=0) stop
      call self%cli%get(switch='-tf', val=self%final_time, error=self%error) ; if (self%error/=0) stop
      call self%cli%get(switch='-r', val=self%results, error=self%error) ; if (self%error/=0) stop
      call self%cli%get(switch='--output', val=self%output, error=self%error) ; if (self%error/=0) stop
      call self%cli%get(switch='--exact_solution', val=self%exact_solution, error=self%error) ; if (self%error/=0) stop
      call self%cli%get(switch='--errors_analysis', val=self%errors_analysis, error=self%error) ; if (self%error/=0) stop

      if (trim(adjustl(self%scheme)) /= 'all') then
         if (.not.is_available(scheme=self%scheme)) then
            integrator_class_names = foodie_integrator_class_names()
            integrator_schemes     = foodie_integrator_schemes()
            print '(A)', 'Error: the scheme "'//trim(adjustl(self%scheme))//'" is unknown!'
            print '(A)', 'Supported classes of schemes are:'
            do i=1, size(integrator_class_names, dim=1)
               print '(A)', '  '//trim(integrator_class_names(i))
            enddo
            print '(A)', 'Supported schemes are:'
            do i=1, size(integrator_schemes, dim=1)
               print '(A)', '  '//trim(integrator_schemes(i))
            enddo
            stop
         endif
      endif

      if (.not.is_dt_valid()) then
         print "(A)", 'Error: the final integration time must be an exact multiple of the time step used!'
         print "(A)", 'Final integration time: '//str(self%final_time, .true.)
         print "(A)", 'Time step: '//str(self%Dt, .true.)
         stop
      endif

      if (self%errors_analysis) then
         if (.not.(size(self%Dt, dim=1) > 1)) self%errors_analysis = .false.
      endif
      endsubroutine parse_cli

      function is_dt_valid()
      !< Verify if the selected time step Dt is valid.
      logical      :: is_dt_valid !< Return true is the selected time step Dt is valid.
      integer(I_P) :: t           !< Counter.

      is_dt_valid = .true.
      do t=1, size(self%Dt)
         is_dt_valid = ((self%final_time - int(self%final_time/self%Dt(t), I_P)*self%Dt(t))==0)
         if (.not.is_dt_valid) exit
      enddo
      endfunction is_dt_valid
   endsubroutine initialize

   subroutine test(self, scheme)
   !< Perform the test.
   class(oscillation_test), intent(in) :: self          !< Test.
   character(*),            intent(in) :: scheme        !< Selected scheme.
   real(R_P), allocatable              :: solution(:,:) !< Solution at each time step.
   real(R_P), allocatable              :: error(:,:)    !< Error (norm L2) with respect the exact solution.
   real(R_P), allocatable              :: order(:,:)    !< Observed order based on subsequent refined solutions.
   type(integrand_oscillation)         :: oscillator    !< Molding integrand oscillator.
   integer(I_P)                        :: last_step     !< Last time step computed.
   integer(I_P)                        :: t             !< Counter.

   print "(A)", trim(adjustl(scheme))

   allocate(error(1:oscillator%integrand_dimension(), 1:size(self%Dt)))
   error = 0.0_R_P
   if (self%errors_analysis) then
      allocate(order(1:oscillator%integrand_dimension(), 1:size(error, dim=2)-1))
      order = 0.0_R_P
   endif

   do t=1, size(self%Dt, dim=1)
      call integrate(scheme=scheme,                       &
                     frequency=self%frequency,            &
                     U0=self%U0,                          &
                     final_time=self%final_time,          &
                     Dt=self%Dt(t),                       &
                     iterations=self%implicit_iterations, &
                     stages=self%stages,                  &
                     is_fast=self%is_fast,                &
                     solution=solution,                   &
                     error=error(:, t),                   &
                     last_step=last_step)
      if (allocated(solution)) then
         print "(A,I10,4(A,G10.3))", "    steps: ", last_step,                                   &
                                     " Dt: ", self%Dt(t), ", f*Dt: ", self%frequency*self%Dt(t), &
                                     ", E(x): ", error(1, t), ", E(y): ", error(2, t)
         if (self%errors_analysis .and. t > 1) then
            order(:, t-1) = observed_order(error=error(:, t-1:t), Dt=self%Dt(t-1:t))
            print "(A,F10.2,A,F10.2)", "      Observed order, O(x): ", order(1, t-1), ", O(y): " , order(2, t-1)
         endif
         call save_results(results=self%results,                    &
                           output=self%output,                      &
                           scheme=trim(adjustl(scheme)),            &
                           frequency=self%frequency,                &
                           U0=self%U0,                              &
                           save_exact_solution=self%exact_solution, &
                           solution=solution(:, 0:last_step))
      endif
   enddo
   endsubroutine test

   ! non type bound procedures
   subroutine check_scheme_has_fast_mode(scheme, integrator)
   !< Check if a scheme support fast mode integrate.
   character(*),             intent(in) :: scheme     !< Scheme name.
   class(integrator_object), intent(in) :: integrator !< Integrator instance.

   if (.not.integrator%has_fast_mode()) then
      write(stderr, '(A)') 'error: '//trim(adjustl(scheme))//' has not fast integrate mode!'
      stop
   endif
   endsubroutine check_scheme_has_fast_mode

   subroutine integrate(scheme, frequency, U0, final_time, Dt, iterations, stages, is_fast, solution, error, last_step)
   !< Integrate domain by means of the given scheme.
   character(*),           intent(in)       :: scheme        !< Selected scheme.
   real(R_P),              intent(in)       :: frequency     !< Oscillation frequency.
   real(R_P),              intent(in)       :: U0(1:)        !< Initial state.
   real(R_P),              intent(in)       :: final_time    !< Final integration time.
   real(R_P),              intent(in)       :: Dt            !< Time step.
   integer(I_P),           intent(in)       :: iterations    !< Number of fixed point iterations.
   integer(I_P),           intent(in)       :: stages        !< Number of stages.
   logical,                intent(in)       :: is_fast       !< Activate fast mode integration.
   real(R_P), allocatable, intent(out)      :: solution(:,:) !< Solution at each time step, X-Y.
   real(R_P),              intent(out)      :: error(1:)     !< Error (norm L2) with respect the exact solution.
   integer(I_P),           intent(out)      :: last_step     !< Last time step computed.
   class(integrator_object), allocatable    :: integrator    !< The integrator.
   type(integrand_oscillation)              :: domain        !< Oscillation field.
   type(integrand_oscillation), allocatable :: previous(:)   !< Previous time steps solutions.
   type(integrand_oscillation), allocatable :: stage(:)      !< Runge-Kutta stages.
   type(integrand_oscillation)              :: buffer        !< Buffer oscillation field.
   type(integrand_oscillation)              :: filter        !< Filter displacement.
   integer                                  :: step          !< Time steps counter.
   integer                                  :: step_offset   !< Time steps counter offset for slicing previous data array.
   real(R_P), allocatable                   :: Dts(:)        !< Time steps for variable stepsize.
   real(R_P)                                :: Dt_a          !< Adaptive time step.

   call domain%initialize(U0=U0, frequency=frequency)

   if (allocated(solution)) deallocate(solution) ; allocate(solution(0:domain%integrand_dimension(), 0:int(final_time/Dt)))
   solution = 0.0_R_P
   solution(1:, 0) = domain%output()

   call foodie_integrator_factory(scheme=scheme, integrator=integrator, stages=stages, tolerance=1e2_R_P)
   if (is_fast) call check_scheme_has_fast_mode(scheme=trim(adjustl(scheme)), integrator=integrator)

   if (integrator%is_multistep()) allocate(previous(1:integrator%steps_number()))
   if (integrator%is_multistage()) allocate(stage(1:integrator%stages_number()))

   select type(integrator)
   type is(integrator_adams_bashforth)
      step = 0
      do while(solution(0, step) < final_time .and. step < ubound(solution, dim=2))
        step = step + 1
        if (integrator%steps_number() >= step) then
           domain = [domain%exact_solution(t=step * Dt)]
           previous(step) = domain
        else
           if (is_fast) then
              call integrator%integrate_fast(U=domain,          &
                                             previous=previous, &
                                             buffer=buffer,     &
                                             Dt=Dt,             &
                                             t=solution(0, step-integrator%steps_number():step-1))
           else
              call integrator%integrate(U=domain,          &
                                        previous=previous, &
                                        Dt=Dt,             &
                                        t=solution(0, step-integrator%steps_number():step-1))
           endif
        endif
        solution(0, step) = step * Dt
        solution(1:, step) = domain%output()
      enddo
      last_step = step

   type is(integrator_adams_bashforth_moulton)
      step = 0
      do while(solution(0, step) < final_time .and. step < ubound(solution, dim=2))
        step = step + 1
        if (integrator%steps_number() >= step) then
           domain = [domain%exact_solution(t=step * Dt)]
           previous(step) = domain
        else
          if (is_fast) then
             call integrator%integrate_fast(U=domain,          &
                                            previous=previous, &
                                            buffer=buffer,     &
                                            Dt=Dt,             &
                                            t=solution(0, step-integrator%steps_number():step-1))
          else
             call integrator%integrate(U=domain,          &
                                       previous=previous, &
                                       Dt=Dt,             &
                                       t=solution(0, step-integrator%steps_number():step-1))
          endif
        endif
        solution(0, step) = step * Dt
        solution(1:, step) = domain%output()
      enddo
      last_step = step

   type is(integrator_adams_moulton)
      if (allocated(previous)) deallocate(previous) ; allocate(previous(1:integrator%steps_number()+1))
      if (integrator%steps_number()==0) then
        step_offset = 1                         ! for 0 step-(a convention)-solver offset is 1
      else
        step_offset = integrator%steps_number() ! for >0 step-solver offset is steps
      endif
      step = 0
      do while(solution(0, step) < final_time .and. step < ubound(solution, dim=2))
        step = step + 1
        if (integrator%steps_number() >= step) then
           domain = [domain%exact_solution(t=step * Dt)]
           previous(step) = domain
        else
          if (is_fast) then
             if (iterations>1) then
               call integrator%integrate_fast(U=domain,                              &
                                              previous=previous,                     &
                                              buffer=buffer,                         &
                                              Dt=Dt,                                 &
                                              t=solution(0,step-step_offset:step-1), &
                                              iterations=iterations)
             else
               call integrator%integrate_fast(U=domain,          &
                                              previous=previous, &
                                              buffer=buffer,     &
                                              Dt=Dt,             &
                                              t=solution(0,step-step_offset:step-1))
             endif
          else
             if (iterations>1) then
               call integrator%integrate(U=domain,                              &
                                         previous=previous,                     &
                                         Dt=Dt,                                 &
                                         t=solution(0,step-step_offset:step-1), &
                                         iterations=iterations)
             else
               call integrator%integrate(U=domain,          &
                                         previous=previous, &
                                         Dt=Dt,             &
                                         t=solution(0,step-step_offset:step-1))
             endif
          endif
        endif
        solution(0, step) = step * Dt
        solution(1:, step) = domain%output()
      enddo
      last_step = step

   type is(integrator_back_df)
      step = 0
      do while(solution(0, step) < final_time .and. step < ubound(solution, dim=2))
        step = step + 1
        if (integrator%steps_number() >= step) then
           domain = [domain%exact_solution(t=step * Dt)]
           previous(step) = domain
        else
          if (is_fast) then
             call integrator%integrate_fast(U=domain,          &
                                            previous=previous, &
                                            buffer=buffer,     &
                                            Dt=Dt,             &
                                            t=solution(0, step-integrator%steps_number():step-1))
          else
             call integrator%integrate(U=domain,          &
                                       previous=previous, &
                                       Dt=Dt,             &
                                       t=solution(0, step-integrator%steps_number():step-1))
          endif
        endif
        solution(0, step) = step * Dt
        solution(1:, step) = domain%output()
      enddo
      last_step = step

   type is(integrator_euler_explicit)
      step = 0
      do while(solution(0, step) < final_time .and. step < ubound(solution, dim=2))
        step = step + 1
        if (is_fast) then
           call integrator%integrate_fast(U=domain, buffer=buffer, Dt=Dt, t=solution(0, step))
        else
           call integrator%integrate(U=domain, Dt=Dt, t=solution(0, step))
        endif
        solution(0, step) = step * Dt
        solution(1:, step) = domain%output()
      enddo
      last_step = step
   type is(integrator_leapfrog)
      step = 0
      do while(solution(0, step) < final_time .and. step < ubound(solution, dim=2))
        step = step + 1
        if (integrator%steps_number() >= step) then
           domain = [domain%exact_solution(t=step * Dt)]
           previous(step) = domain
        else
           if (index(scheme, 'raw') > 0 ) then
              if (is_fast) then
                call integrator%integrate_fast(U=domain, previous=previous, buffer=buffer, Dt=Dt, t=solution(0, step),filter=filter)
              else
                call integrator%integrate(U=domain, previous=previous, Dt=Dt, t=solution(0, step), filter=filter)
              endif
           else
              if (is_fast) then
                call integrator%integrate_fast(U=domain, previous=previous, buffer=buffer, Dt=Dt, t=solution(0, step))
              else
                call integrator%integrate(U=domain, previous=previous, Dt=Dt, t=solution(0, step))
              endif
           endif
        endif
        solution(0, step) = step * Dt
        solution(1:, step) = domain%output()
      enddo
      last_step = step

   type is(integrator_lmm_ssp)
      step = 0
      do while(solution(0, step) < final_time .and. step < ubound(solution, dim=2))
        step = step + 1
        if (integrator%steps_number() >= step) then
           domain = [domain%exact_solution(t=step * Dt)]
           previous(step) = domain
        else
          if (is_fast) then
             call integrator%integrate_fast(U=domain,          &
                                            previous=previous, &
                                            buffer=buffer,     &
                                            Dt=Dt,             &
                                            t=solution(0, step-integrator%steps_number():step-1))
          else
             call integrator%integrate(U=domain,          &
                                       previous=previous, &
                                       Dt=Dt,             &
                                       t=solution(0, step-integrator%steps_number():step-1))
          endif
        endif
        solution(0, step) = step * Dt
        solution(1:, step) = domain%output()
      enddo
      last_step = step

   type is(integrator_lmm_ssp_vss)
      if (allocated(Dts)) deallocate(Dts) ; allocate(Dts(1:integrator%steps_number())) ; Dts = Dt
      step = 0
      do while(solution(0, step) < final_time .and. step < ubound(solution, dim=2))
        step = step + 1
        if (integrator%steps_number() >= step) then
           domain = [domain%exact_solution(t=step * Dt)]
           previous(step) = domain
        else
          if (is_fast) then
             call integrator%integrate_fast(U=domain,          &
                                            previous=previous, &
                                            buffer=buffer,     &
                                            Dt=Dts,            &
                                            t=solution(0, step-integrator%steps_number():step-1))
          else
             call integrator%integrate(U=domain,          &
                                       previous=previous, &
                                       Dt=Dts,            &
                                       t=solution(0, step-integrator%steps_number():step-1))
          endif
        endif
        solution(0, step) = step * Dt
        solution(1:, step) = domain%output()
      enddo
      last_step = step

   type is(integrator_ms_runge_kutta_ssp)
      step = 0
      do while(solution(0, step) < final_time .and. step < ubound(solution, dim=2))
        step = step + 1
        if (integrator%steps_number() >= step) then
           domain = [domain%exact_solution(t=step * Dt)]
           previous(step) = domain
        else
          if (is_fast) then
             call integrator%integrate_fast(U=domain,          &
                                            previous=previous, &
                                            stage=stage,       &
                                            buffer=buffer,     &
                                            Dt=Dt,             &
                                            t=solution(0, step-integrator%steps_number():step-1))
          else
             call integrator%integrate(U=domain,          &
                                       previous=previous, &
                                       stage=stage,       &
                                       Dt=Dt,             &
                                       t=solution(0, step-integrator%steps_number():step-1))
          endif
        endif
        solution(0, step) = step * Dt
        solution(1:, step) = domain%output()
      enddo
      last_step = step

   type is(integrator_runge_kutta_emd)
       step = 0
      do while(solution(0, step) < final_time .and. step < ubound(solution, dim=2))
        step = step + 1
        Dt_a = Dt
        if (is_fast) then
           call integrator%integrate_fast(U=domain, stage=stage, buffer=buffer, Dt=Dt_a, t=solution(0, step))
        else
           call integrator%integrate(U=domain, stage=stage, Dt=Dt_a, t=solution(0, step))
        endif
        solution(0, step) = step * Dt
        solution(1:, step) = domain%output()
      enddo
      last_step = step

   type is(integrator_runge_kutta_ls)
      if (allocated(stage)) deallocate(stage) ; allocate(stage(1:integrator%registers_number()))
      step = 0
      do while(solution(0, step) < final_time .and. step < ubound(solution, dim=2))
        step = step + 1
        if (is_fast) then
           call integrator%integrate_fast(U=domain, stage=stage, buffer=buffer, Dt=Dt, t=solution(0, step))
        else
           call integrator%integrate(U=domain, stage=stage, Dt=Dt, t=solution(0, step))
        endif
        solution(0, step) = step * Dt
        solution(1:, step) = domain%output()
      enddo
      last_step = step

   type is(integrator_runge_kutta_lssp)
      step = 0
      do while(solution(0, step) < final_time .and. step < ubound(solution, dim=2))
        step = step + 1
        if (is_fast) then
           call integrator%integrate_fast(U=domain, stage=stage, buffer=buffer, Dt=Dt, t=solution(0, step))
        else
           call integrator%integrate(U=domain, stage=stage, Dt=Dt, t=solution(0, step))
        endif
        solution(0, step) = step * Dt
        solution(1:, step) = domain%output()
      enddo
      last_step = step

   type is(integrator_runge_kutta_ssp)
      step = 0
      do while(solution(0, step) < final_time .and. step < ubound(solution, dim=2))
        step = step + 1
        if (is_fast) then
           call integrator%integrate_fast(U=domain, stage=stage, buffer=buffer, Dt=Dt, t=solution(0, step))
        else
           call integrator%integrate(U=domain, stage=stage, Dt=Dt, t=solution(0, step))
        endif
        solution(0, step) = step * Dt
        solution(1:, step) = domain%output()
      enddo
      last_step = step
   endselect

   error = 0._R_P
   do step=0, last_step
      domain = solution(1:, step)
      error = error + (domain%output() - domain%exact_solution(t=solution(0, step))) ** 2
   enddo
   error = sqrt(error)
   endsubroutine integrate

   subroutine save_results(results, output, scheme, frequency, U0, save_exact_solution, solution)
   !< Save results (and plots).
   logical,      intent(in)      :: results             !< Flag for activating results saving.
   character(*), intent(in)      :: output              !< Output files basename coming from CLI.
   character(*), intent(in)      :: scheme              !< Selected scheme: must be defined into *solvers*.
   real(R_P),    intent(in)      :: frequency           !< Oscillation frequency.
   real(R_P),    intent(in)      :: U0(1:)              !< Initial state.
   logical,      intent(in)      :: save_exact_solution !< Flag for saving exact solution.
   real(R_P),    intent(in)      :: solution(0:,0:)     !< Solution at each time step.
   character(len=:), allocatable :: title               !< Output files title.
   character(len=:), allocatable :: basename            !< Output files basename.
   integer(I_P)                  :: rawfile             !< Raw file unit for saving results.
   type(integrand_oscillation)   :: oscillator          !< Oscillation field.
   integer(I_P)                  :: s                   !< Counter.

   basename = trim(adjustl(output))//'-'//trim(strz(ubound(solution, dim=2), 10))//'-time_steps-'//trim(adjustl(scheme))
   title = 'oscillation equations integration, solver='//trim(adjustl(scheme))
   if (results) then
     open(newunit=rawfile, file=basename//'.dat')
     write(rawfile, '(A)')'TITLE="'//title//'"'
     write(rawfile, '(A)')'VARIABLES="t" "x" "y" "amplitude" "phase"'
     write(rawfile, '(A)')'ZONE T="'//trim(adjustl(scheme))//'"'
     do s=0, ubound(solution, dim=2)
       write(rawfile, '(5('//FR_P//',1X))')solution(:, s), amplitude_phase(solution(1:2, s))
     enddo
     close(rawfile)
   endif
   if (save_exact_solution) then
      call oscillator%initialize(U0=U0, frequency=frequency)
      basename = trim(adjustl(output))//'-'//trim(strz(ubound(solution, dim=2), 10))//'-time_steps-exact_solution'
      title = 'linear constant coefficients equation integration, solver=exact solution'
      open(newunit=rawfile, file=basename//'.dat')
      write(rawfile, '(A)')'TITLE="'//title//'"'
      write(rawfile, '(A)')'VARIABLES="t" "u"'
      write(rawfile, '(A)')'ZONE T="exact solution"'
      do s=0, ubound(solution, dim=2)
         write(rawfile, '(5('//FR_P//',1X))')solution(0, s), oscillator%exact_solution(t=solution(0, s)), &
                                             amplitude_phase(oscillator%exact_solution(t=solution(0, s)))
      enddo
      close(rawfile)
   endif
   contains
      function amplitude_phase(sol) result(ap)
      !< Compute amplitude and phase of the solution provided in X-Y domain.
      real(R_P), intent(in) :: sol(1:) !< Solution in X-Y domain.
      real(R_P)             :: ap(1:2) !< Amplitude and phase solution.

      ap(1) = sqrt(sol(1)**2 + sol(2)**2)
      ap(2) = atan(-sol(1) / sol(2))
      endfunction amplitude_phase
   endsubroutine save_results

  pure function observed_order(error, Dt)
  !< Estimate the order of accuracy using 2 subsequent refined numerical solutions.
  real(R_P), intent(in) :: error(1:, 1:)                        !< Computed errors.
  real(R_P), intent(in) :: Dt(1:)                               !< Time steps used.
  real(R_P)             :: observed_order(1:size(error, dim=1)) !< Estimation of the order of accuracy.
  integer(I_P)          :: v                                    !< Variables counter.

  do v=1, size(error, dim=1)
    observed_order(v) = log(error(v, 1) / error(v, 2)) / log(Dt(1) / Dt(2))
  enddo
  endfunction observed_order
endmodule foodie_test_oscillation_test

program foodie_test_oscillation
!< Test FOODIE with the integration of Oscillation equations.

use foodie_test_oscillation_test, only : oscillation_test

implicit none
type(oscillation_test) :: test !< Oscillation test.

call test%execute
endprogram foodie_test_oscillation