diff --git a/fobos b/fobos index 49a1f246..3902fbbc 100644 --- a/fobos +++ b/fobos @@ -218,17 +218,17 @@ rule = tar --xform="s%^%FOODIE/%" -czf FOODIE.tar.gz * [rule-makecoverage] help = Rule for performing coverage analysis -rule_1 = FoBiS.py build -f src/tests/accuracy/oscillation/fobos -mode gnu-coverage -rule_2 = ./build/tests/accuracy/oscillation/oscillation -s all -rule_3 = rm -f build/tests/accuracy/oscillation/obj/penf* build/tests/accuracy/oscillation/obj/face* build/tests/accuracy/oscillation/obj/flap* build/tests/accuracy/oscillation/obj/wenoof* -rule_4 = gcov -o build/tests/accuracy/oscillation/obj/ src/lib/foodie* +rule_1 = FoBiS.py build -f src/tests/tester/fobos -mode gnu-coverage +rule_2 = ./build/tests/tester/foodie_tester +rule_3 = rm -f build/tests/tester/obj/penf* build/tests/tester/obj/face* build/tests/tester/obj/flap* build/tests/tester/obj/wenoof* +rule_4 = gcov -o build/tests/tester/obj/ src/lib/foodie* rule_5 = rm -f *.gcov [rule-coverage-analysis] help = Rule for performing coverage analysis and saving reports in markdown -rule_1 = FoBiS.py build -f src/tests/accuracy/oscillation/fobos -mode gnu-coverage -rule_2 = ./build/tests/accuracy/oscillation/oscillation -s all -rule_3 = rm -f build/tests/accuracy/oscillation/obj/penf* build/tests/accuracy/oscillation/obj/face* build/tests/accuracy/oscillation/obj/flap* build/tests/accuracy/oscillation/obj/wenoof* -rule_4 = gcov -o build/tests/accuracy/oscillation/obj/ src/lib/foodie* +rule_1 = FoBiS.py build -f src/tests/tester/fobos -mode gnu-coverage +rule_2 = ./build/tests/tester/foodie_tester +rule_3 = rm -f build/tests/tester/obj/penf* build/tests/tester/obj/face* build/tests/tester/obj/flap* build/tests/tester/obj/wenoof* +rule_4 = gcov -o build/tests/tester/obj/ src/lib/foodie* rule_5 = FoBiS.py rule -gcov_analyzer wiki/ Coverage-Analysis rule_6 = rm -f *.gcov diff --git a/src/lib/foodie.f90 b/src/lib/foodie.f90 index b147e226..c32c7b91 100644 --- a/src/lib/foodie.f90 +++ b/src/lib/foodie.f90 @@ -74,6 +74,9 @@ module foodie use foodie_integrator_lmm_ssp, only : integrator_lmm_ssp use foodie_integrator_lmm_ssp_vss, only : integrator_lmm_ssp_vss use foodie_integrator_ms_runge_kutta_ssp, only : integrator_ms_runge_kutta_ssp +use foodie_integrator_multistage_object, only : integrator_multistage_object +use foodie_integrator_multistage_multistep_object, only : integrator_multistage_multistep_object +use foodie_integrator_multistep_object, only : integrator_multistep_object use foodie_integrator_runge_kutta_emd, only : integrator_runge_kutta_emd use foodie_integrator_runge_kutta_low_storage, only : integrator_runge_kutta_ls use foodie_integrator_runge_kutta_lssp, only : integrator_runge_kutta_lssp @@ -82,11 +85,20 @@ module foodie implicit none private +! helper procedures public :: foodie_integrator_class_names public :: foodie_integrator_factory public :: foodie_integrator_schemes +public :: is_available +public :: is_class_available +public :: is_scheme_available +! abstract objects public :: integrand_object public :: integrator_object +public :: integrator_multistage_object +public :: integrator_multistage_multistep_object +public :: integrator_multistep_object +! concrete objects public :: integrator_adams_bashforth public :: integrator_adams_bashforth_moulton public :: integrator_adams_moulton @@ -100,9 +112,6 @@ module foodie public :: integrator_runge_kutta_ls public :: integrator_runge_kutta_lssp public :: integrator_runge_kutta_ssp -public :: is_available -public :: is_class_available -public :: is_scheme_available contains pure function foodie_integrator_class_names() result(names) @@ -137,7 +146,7 @@ pure function foodie_integrator_class_names() result(names) names = [names, int_runge_kutta_ssp % class_name()] endfunction foodie_integrator_class_names - subroutine foodie_integrator_factory(scheme, integrator, stages, tolerance, nu, alpha) + subroutine foodie_integrator_factory(scheme, integrator, stages, tolerance, nu, alpha, iterations, autoupdate, U) !< Return a concrete instance of [[integrator_object]] given a scheme selection. !< !< This is the FOODIE integrators factory. @@ -145,10 +154,13 @@ subroutine foodie_integrator_factory(scheme, integrator, stages, tolerance, nu, !< @note If an error occurs the error status of [[integrator_object]] is updated. character(*), intent(in) :: scheme !< Selected integrator given. class(integrator_object), allocatable, intent(out) :: integrator !< The FOODIE integrator. - integer(I_P), optional, intent(in) :: stages !< Stages of multi-stage methods. - real(R_P), optional, intent(in) :: tolerance !< Tolerance on the local truncation error. - real(R_P), optional, intent(in) :: nu !< Williams-Robert-Asselin filter coefficient. - real(R_P), optional, intent(in) :: alpha !< Robert-Asselin filter coefficient. + integer(I_P), optional, intent(in) :: stages !< Stages of multi-stage methods. + real(R_P), optional, intent(in) :: tolerance !< Tolerance on the local truncation error. + real(R_P), optional, intent(in) :: nu !< Williams-Robert-Asselin filter coefficient. + real(R_P), optional, intent(in) :: alpha !< Robert-Asselin filter coefficient. + integer(I_P), optional, intent(in) :: iterations !< Implicit iterations. + logical, optional, intent(in) :: autoupdate !< Enable cyclic autoupdate for multistep. + class(integrand_object), optional, intent(in) :: U !< Integrand molding prototype. type(integrator_adams_bashforth) :: int_adams_bashforth !< Integrator Adams Bashforth. type(integrator_adams_bashforth_moulton) :: int_adams_bashforth_moulton !< Integrator Adams Bashforth Moulton. type(integrator_adams_moulton) :: int_adams_moulton !< Integrator Adams Moulton. @@ -167,75 +179,79 @@ subroutine foodie_integrator_factory(scheme, integrator, stages, tolerance, nu, allocate(integrator_adams_bashforth_moulton :: integrator) select type(integrator) type is(integrator_adams_bashforth_moulton) - call integrator%initialize(scheme=scheme) + call integrator%initialize(scheme=scheme, iterations=iterations, autoupdate=autoupdate, U=U) endselect elseif (index(trim(adjustl(scheme)), trim(int_adams_bashforth%class_name())) > 0) then allocate(integrator_adams_bashforth :: integrator) select type(integrator) type is(integrator_adams_bashforth) - call integrator%initialize(scheme=scheme) + call integrator%initialize(scheme=scheme, autoupdate=autoupdate, U=U) endselect elseif (index(trim(adjustl(scheme)), trim(int_adams_moulton%class_name())) > 0) then allocate(integrator_adams_moulton :: integrator) select type(integrator) type is(integrator_adams_moulton) - call integrator%initialize(scheme=scheme) + call integrator%initialize(scheme=scheme, iterations=iterations, autoupdate=autoupdate, U=U) endselect elseif (index(trim(adjustl(scheme)), trim(int_back_df%class_name())) > 0) then allocate(integrator_back_df :: integrator) select type(integrator) type is(integrator_back_df) - call integrator%initialize(scheme=scheme) + call integrator%initialize(scheme=scheme, iterations=iterations, autoupdate=autoupdate, U=U) endselect elseif (index(trim(adjustl(scheme)), trim(int_euler_explicit%class_name())) > 0) then allocate(integrator_euler_explicit :: integrator) + select type(integrator) + type is(integrator_euler_explicit) + call integrator%initialize(scheme=scheme, U=U) + endselect elseif (index(trim(adjustl(scheme)), trim(int_leapfrog%class_name())) > 0) then allocate(integrator_leapfrog :: integrator) select type(integrator) type is(integrator_leapfrog) - call integrator%initialize(scheme=scheme, nu=nu, alpha=alpha) + call integrator%initialize(scheme=scheme, nu=nu, alpha=alpha, autoupdate=autoupdate, U=U) endselect elseif (index(trim(adjustl(scheme)), trim(int_lmm_ssp_vss%class_name())) > 0) then allocate(integrator_lmm_ssp_vss :: integrator) select type(integrator) type is(integrator_lmm_ssp_vss) - call integrator%initialize(scheme=scheme) + call integrator%initialize(scheme=scheme, autoupdate=autoupdate, U=U) endselect elseif (index(trim(adjustl(scheme)), trim(int_lmm_ssp%class_name())) > 0) then allocate(integrator_lmm_ssp :: integrator) select type(integrator) type is(integrator_lmm_ssp) - call integrator%initialize(scheme=scheme) + call integrator%initialize(scheme=scheme, autoupdate=autoupdate, U=U) endselect elseif (index(trim(adjustl(scheme)), trim(int_ms_runge_kutta_ssp%class_name())) > 0) then allocate(integrator_ms_runge_kutta_ssp :: integrator) select type(integrator) type is(integrator_ms_runge_kutta_ssp) - call integrator%initialize(scheme=scheme) + call integrator%initialize(scheme=scheme, iterations=iterations, autoupdate=autoupdate, U=U) endselect elseif (index(trim(adjustl(scheme)), trim(int_runge_kutta_emd%class_name())) > 0) then allocate(integrator_runge_kutta_emd :: integrator) select type(integrator) type is(integrator_runge_kutta_emd) - call integrator%initialize(scheme=scheme, tolerance=tolerance) + call integrator%initialize(scheme=scheme, tolerance=tolerance, U=U) endselect elseif (index(trim(adjustl(scheme)), trim(int_runge_kutta_lssp%class_name())) > 0) then allocate(integrator_runge_kutta_lssp :: integrator) select type(integrator) type is(integrator_runge_kutta_lssp) - call integrator%initialize(scheme=scheme, stages=stages) + call integrator%initialize(scheme=scheme, stages=stages, U=U) endselect elseif (index(trim(adjustl(scheme)), trim(int_runge_kutta_ls%class_name())) > 0) then allocate(integrator_runge_kutta_ls :: integrator) select type(integrator) type is(integrator_runge_kutta_ls) - call integrator%initialize(scheme=scheme) + call integrator%initialize(scheme=scheme, U=U) endselect elseif (index(trim(adjustl(scheme)), trim(int_runge_kutta_ssp%class_name())) > 0) then allocate(integrator_runge_kutta_ssp :: integrator) select type(integrator) type is(integrator_runge_kutta_ssp) - call integrator%initialize(scheme=scheme) + call integrator%initialize(scheme=scheme, U=U) endselect else write(stderr, '(A)')'error: "'//trim(adjustl(scheme))//'" scheme is unknown!' diff --git a/src/lib/foodie_integrator_adams_bashforth.f90 b/src/lib/foodie_integrator_adams_bashforth.f90 index bcad1eba..eee8348d 100644 --- a/src/lib/foodie_integrator_adams_bashforth.f90 +++ b/src/lib/foodie_integrator_adams_bashforth.f90 @@ -28,6 +28,7 @@ module foodie_integrator_adams_bashforth use foodie_error_codes, only : ERROR_UNSUPPORTED_SCHEME use foodie_integrand_object, only : integrand_object +use foodie_integrator_multistep_object, only : integrator_multistep_object use foodie_integrator_object, only : integrator_object use penf, only : I_P, R_P @@ -53,391 +54,317 @@ module foodie_integrator_adams_bashforth trim(class_name_)//'_15', & trim(class_name_)//'_16'] !< List of supported schemes. -logical, parameter :: has_fast_mode_=.true. !< Flag to check if integrator provides *fast mode* integrate. -logical, parameter :: is_multistage_=.false. !< Flag to check if integrator is multistage. -logical, parameter :: is_multistep_=.true. !< Flag to check if integrator is multistep. - -type, extends(integrator_object) :: integrator_adams_bashforth - !< FOODIE integrator: provide an explicit class of Adams-Bashforth multi-step schemes, from 1st to 16th order accurate. - !< - !< @note The integrator must be created or initialized (initialize the *b* coefficients) before used. - private - integer(I_P) :: steps=0 !< Number of time steps. - real(R_P), allocatable :: b(:) !< *b* coefficients. - contains - ! deferred methods - procedure, pass(self) :: class_name !< Return the class name of schemes. - procedure, pass(self) :: description !< Return pretty-printed object description. - procedure, pass(self) :: has_fast_mode !< Return .true. if the integrator class has *fast mode* integrate. - procedure, pass(lhs) :: integr_assign_integr !< Operator `=`. - procedure, pass(self) :: is_multistage !< Return .true. for multistage integrator. - procedure, pass(self) :: is_multistep !< Return .true. for multistep integrator. - procedure, pass(self) :: is_supported !< Return .true. if the integrator class support the given scheme. - procedure, pass(self) :: stages_number !< Return number of stages used. - procedure, pass(self) :: steps_number !< Return number of steps used. - procedure, pass(self) :: supported_schemes !< Return the list of supported schemes. - ! public methods - procedure, pass(self) :: destroy !< Destroy the integrator. - procedure, pass(self) :: initialize !< Initialize (create) the integrator. - procedure, pass(self) :: integrate !< Integrate integrand field. - procedure, pass(self) :: integrate_fast !< Integrate integrand field, fast mode. - procedure, pass(self) :: update_previous !< Cyclic update previous time steps. +logical, parameter :: has_fast_mode_=.true. !< Flag to check if integrator provides *fast mode* integrate. + +type, extends(integrator_multistep_object) :: integrator_adams_bashforth + !< FOODIE integrator: provide an explicit class of Adams-Bashforth multi-step schemes, from 1st to 16th order accurate. + !< + !< @note The integrator must be created or initialized (initialize the *b* coefficients) before used. + !< + !< @note The time steps `Dt(1:steps)` passed to the integrate methods must be identical: this integrator supports only + !< fixed time steps. + private + real(R_P), allocatable :: b(:) !< *b* coefficients. + contains + ! deferred methods + procedure, pass(self) :: class_name !< Return the class name of schemes. + procedure, pass(self) :: has_fast_mode !< Return .true. if the integrator class has *fast mode* integrate. + procedure, pass(lhs) :: integr_assign_integr !< Operator `=`. + procedure, pass(self) :: integrate !< Integrate integrand field. + procedure, pass(self) :: integrate_fast !< Integrate integrand field, fast mode. + procedure, pass(self) :: is_supported !< Return .true. if the integrator class support the given scheme. + procedure, pass(self) :: supported_schemes !< Return the list of supported schemes. + ! public methods + procedure, pass(self) :: destroy !< Destroy the integrator. + procedure, pass(self) :: initialize !< Initialize (create) the integrator. endtype integrator_adams_bashforth contains - ! deferred methods - pure function class_name(self) - !< Return the class name of schemes. - class(integrator_adams_bashforth), intent(in) :: self !< Integrator. - character(len=99) :: class_name !< Class name. - - class_name = trim(adjustl(class_name_)) - endfunction class_name - - pure function description(self, prefix) result(desc) - !< Return a pretty-formatted object description. - class(integrator_adams_bashforth), intent(in) :: self !< Integrator. - character(*), intent(in), optional :: prefix !< Prefixing string. - character(len=:), allocatable :: desc !< Description. - character(len=:), allocatable :: prefix_ !< Prefixing string, local variable. - character(len=1), parameter :: NL=new_line('a') !< New line character. - integer(I_P) :: s !< Counter. - - prefix_ = '' ; if (present(prefix)) prefix_ = prefix - desc = '' - desc = desc//prefix_//'Adams-Bashforth multi-step schemes class'//NL - desc = desc//prefix_//' Supported schemes:'//NL - do s=lbound(supported_schemes_, dim=1), ubound(supported_schemes_, dim=1) - 1 - desc = desc//prefix_//' + '//supported_schemes_(s)//NL - enddo - desc = desc//prefix_//' + '//supported_schemes_(ubound(supported_schemes_, dim=1)) - endfunction description - - elemental function has_fast_mode(self) - !< Return .true. if the integrator class has *fast mode* integrate. - class(integrator_adams_bashforth), intent(in) :: self !< Integrator. - logical :: has_fast_mode !< Inquire result. - - has_fast_mode = has_fast_mode_ - endfunction has_fast_mode - - pure subroutine integr_assign_integr(lhs, rhs) - !< Operator `=`. - class(integrator_adams_bashforth), intent(inout) :: lhs !< Left hand side. - class(integrator_object), intent(in) :: rhs !< Right hand side. - - call lhs%assign_abstract(rhs=rhs) - select type(rhs) - class is (integrator_adams_bashforth) - lhs%steps = rhs%steps - if (allocated(rhs%b)) lhs%b = rhs%b - endselect - endsubroutine integr_assign_integr - - elemental function is_multistage(self) - !< Return .true. for multistage integrator. - class(integrator_adams_bashforth), intent(in) :: self !< Integrator. - logical :: is_multistage !< Inquire result. - - is_multistage = is_multistage_ - endfunction is_multistage - - elemental function is_multistep(self) - !< Return .true. for multistage integrator. - class(integrator_adams_bashforth), intent(in) :: self !< Integrator. - logical :: is_multistep !< Inquire result. - - is_multistep = is_multistep_ - endfunction is_multistep - - elemental function is_supported(self, scheme) - !< Return .true. if the integrator class support the given scheme. - class(integrator_adams_bashforth), intent(in) :: self !< Integrator. - character(*), intent(in) :: scheme !< Selected scheme. - logical :: is_supported !< Inquire result. - integer(I_P) :: s !< Counter. - - is_supported = .false. - do s=lbound(supported_schemes_, dim=1), ubound(supported_schemes_, dim=1) - if (trim(adjustl(scheme)) == trim(adjustl(supported_schemes_(s)))) then - is_supported = .true. - return - endif - enddo - endfunction is_supported - - elemental function stages_number(self) - !< Return number of stages used. - class(integrator_adams_bashforth), intent(in) :: self !< Integrator. - integer(I_P) :: stages_number !< Number of stages used. - - stages_number = 0 - endfunction stages_number - - elemental function steps_number(self) - !< Return number of steps used. - class(integrator_adams_bashforth), intent(in) :: self !< Integrator. - integer(I_P) :: steps_number !< Number of steps used. - - steps_number = self%steps - endfunction steps_number - - pure function supported_schemes(self) result(schemes) - !< Return the list of supported schemes. - class(integrator_adams_bashforth), intent(in) :: self !< Integrator. - character(len=99), allocatable :: schemes(:) !< Queried scheme. - - allocate(schemes(lbound(supported_schemes_, dim=1):ubound(supported_schemes_, dim=1))) - schemes = supported_schemes_ - endfunction supported_schemes - - ! public methods - elemental subroutine destroy(self) - !< Destroy the integrator. - class(integrator_adams_bashforth), intent(inout) :: self !< Integrator. - - call self%destroy_abstract - self%steps = 0 - if (allocated(self%b)) deallocate(self%b) - endsubroutine destroy - - subroutine initialize(self, scheme) - !< Create the actual Adams-Bashforth integrator: initialize the *b* coefficients. - !< - !< @note If the integrator is initialized with a bad (unsupported) number of required time steps the initialization fails and - !< the integrator error status is updated consistently for external-provided errors handling. - class(integrator_adams_bashforth), intent(inout) :: self !< Integrator. - character(*), intent(in) :: scheme !< Selected scheme. - - if (self%is_supported(scheme=scheme)) then - call self%destroy - select case(trim(adjustl(scheme))) - case('adams_bashforth_1') - self%steps = 1 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P - self%b(1) = 1.0_R_P - case('adams_bashforth_2') - self%steps = 2 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P - self%b(1) = -1.0_R_P/2.0_R_P - self%b(2) = 3.0_R_P/2.0_R_P - case('adams_bashforth_3') - self%steps = 3 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P - self%b(1) = 5.0_R_P/12.0_R_P - self%b(2) = -16.0_R_P/12.0_R_P - self%b(3) = 23.0_R_P/12.0_R_P - case('adams_bashforth_4') - self%steps = 4 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P - self%b(1) = -9.0_R_P/24.0_R_P - self%b(2) = 37.0_R_P/24.0_R_P - self%b(3) = -59.0_R_P/24.0_R_P - self%b(4) = 55.0_R_P/24.0_R_P - case('adams_bashforth_5') - self%steps = 5 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P - self%b(1) = 251.0_R_P/720.0_R_P - self%b(2) = -1274.0_R_P/720.0_R_P - self%b(3) = 2616.0_R_P/720.0_R_P - self%b(4) = -2774.0_R_P/720.0_R_P - self%b(5) = 1901.0_R_P/720.0_R_P - case('adams_bashforth_6') - self%steps = 6 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P - self%b(1) = -475.0_R_P/1440.0_R_P - self%b(2) = 2877.0_R_P/1440.0_R_P - self%b(3) = -7298.0_R_P/1440.0_R_P - self%b(4) = 9982.0_R_P/1440.0_R_P - self%b(5) = -7923.0_R_P/1440.0_R_P - self%b(6) = 4277.0_R_P/1440.0_R_P - case('adams_bashforth_7') - self%steps = 7 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P - self%b(1) = 19087.0_R_P/60480.0_R_P - self%b(2) = -134472.0_R_P/60480.0_R_P - self%b(3) = 407139.0_R_P/60480.0_R_P - self%b(4) = -688256.0_R_P/60480.0_R_P - self%b(5) = 705549.0_R_P/60480.0_R_P - self%b(6) = -447288.0_R_P/60480.0_R_P - self%b(7) = 198721.0_R_P/60480.0_R_P - case('adams_bashforth_8') - self%steps = 8 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P - self%b(1) = -36799.0_R_P/120960.0_R_P - self%b(2) = 295767.0_R_P/120960.0_R_P - self%b(3) = -1041723.0_R_P/120960.0_R_P - self%b(4) = 2102243.0_R_P/120960.0_R_P - self%b(5) = -2664477.0_R_P/120960.0_R_P - self%b(6) = 2183877.0_R_P/120960.0_R_P - self%b(7) = -1152169.0_R_P/120960.0_R_P - self%b(8) = 434241.0_R_P/120960.0_R_P - case('adams_bashforth_9') - self%steps = 9 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P - self%b(1) = 1070017.0_R_P/3628800.0_R_P - self%b(2) = -9664106.0_R_P/3628800.0_R_P - self%b(3) = 38833486.0_R_P/3628800.0_R_P - self%b(4) = -91172642.0_R_P/3628800.0_R_P - self%b(5) = 137968480.0_R_P/3628800.0_R_P - self%b(6) = -139855262.0_R_P/3628800.0_R_P - self%b(7) = 95476786.0_R_P/3628800.0_R_P - self%b(8) = -43125206.0_R_P/3628800.0_R_P - self%b(9) = 14097247.0_R_P/3628800.0_R_P - case('adams_bashforth_10') - self%steps = 10 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P - self%b(1) = -2082753.0_R_P/7257600.0_R_P - self%b(2) = 20884811.0_R_P/7257600.0_R_P - self%b(3) = -94307320.0_R_P/7257600.0_R_P - self%b(4) = 252618224.0_R_P/7257600.0_R_P - self%b(5) = -444772162.0_R_P/7257600.0_R_P - self%b(6) = 538363838.0_R_P/7257600.0_R_P - self%b(7) = -454661776.0_R_P/7257600.0_R_P - self%b(8) = 265932680.0_R_P/7257600.0_R_P - self%b(9) = -104995189.0_R_P/7257600.0_R_P - self%b(10) = 30277247.0_R_P/7257600.0_R_P - case('adams_bashforth_11') - self%steps = 11 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P - self%b(1) = 134211265.0_R_P/479001600.0_R_P - self%b(2) = -1479574348.0_R_P/479001600.0_R_P - self%b(3) = 7417904451.0_R_P/479001600.0_R_P - self%b(4) = -22329634920.0_R_P/479001600.0_R_P - self%b(5) = 44857168434.0_R_P/479001600.0_R_P - self%b(6) = -63176201472.0_R_P/479001600.0_R_P - self%b(7) = 63716378958.0_R_P/479001600.0_R_P - self%b(8) = -46113029016.0_R_P/479001600.0_R_P - self%b(9) = 23591063805.0_R_P/479001600.0_R_P - self%b(10) = -8271795124.0_R_P/479001600.0_R_P - self%b(11) = 2132509567.0_R_P/479001600.0_R_P - case('adams_bashforth_12') - self%steps = 12 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P - self%b(1) = -262747265.0_R_P/958003200.0_R_P - self%b(2) = 3158642445.0_R_P/958003200.0_R_P - self%b(3) = -17410248271.0_R_P/958003200.0_R_P - self%b(4) = 58189107627.0_R_P/958003200.0_R_P - self%b(5) = -131365867290.0_R_P/958003200.0_R_P - self%b(6) = 211103573298.0_R_P/958003200.0_R_P - self%b(7) = -247741639374.0_R_P/958003200.0_R_P - self%b(8) = 214139355366.0_R_P/958003200.0_R_P - self%b(9) = -135579356757.0_R_P/958003200.0_R_P - self%b(10) = 61633227185.0_R_P/958003200.0_R_P - self%b(11) = -19433810163.0_R_P/958003200.0_R_P - self%b(12) = 4527766399.0_R_P/958003200.0_R_P - case('adams_bashforth_13') - self%steps = 13 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P - self%b(1) = 703604254357.0_R_P/2615348736000.0_R_P - self%b(2) = -9160551085734.0_R_P/2615348736000.0_R_P - self%b(3) = 55060974662412.0_R_P/2615348736000.0_R_P - self%b(4) = -202322913738370.0_R_P/2615348736000.0_R_P - self%b(5) = 507140369728425.0_R_P/2615348736000.0_R_P - self%b(6) = -915883387152444.0_R_P/2615348736000.0_R_P - self%b(7) = 1226443086129408.0_R_P/2615348736000.0_R_P - self%b(8) = -1233589244941764.0_R_P/2615348736000.0_R_P - self%b(9) = 932884546055895.0_R_P/2615348736000.0_R_P - self%b(10) = -524924579905150.0_R_P/2615348736000.0_R_P - self%b(11) = 214696591002612.0_R_P/2615348736000.0_R_P - self%b(12) = -61497552797274.0_R_P/2615348736000.0_R_P - self%b(13) = 13064406523627.0_R_P/2615348736000.0_R_P - case('adams_bashforth_14') - self%steps = 14 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P - self%b(1) = -1382741929621.0_R_P/5230697472000.0_R_P - self%b(2) = 19382853593787.0_R_P/5230697472000.0_R_P - self%b(3) = -126174972681906.0_R_P/5230697472000.0_R_P - self%b(4) = 505586141196430.0_R_P/5230697472000.0_R_P - self%b(5) = -1393306307155755.0_R_P/5230697472000.0_R_P - self%b(6) = 2793869602879077.0_R_P/5230697472000.0_R_P - self%b(7) = -4204551925534524.0_R_P/5230697472000.0_R_P - self%b(8) = 4825671323488452.0_R_P/5230697472000.0_R_P - self%b(9) = -4246767353305755.0_R_P/5230697472000.0_R_P - self%b(10) = 2854429571790805.0_R_P/5230697472000.0_R_P - self%b(11) = -1445313351681906.0_R_P/5230697472000.0_R_P - self%b(12) = 537247052515662.0_R_P/5230697472000.0_R_P - self%b(13) = -140970750679621.0_R_P/5230697472000.0_R_P - self%b(14) = 27511554976875.0_R_P/5230697472000.0_R_P - case('adams_bashforth_15') - self%steps = 15 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P - self%b(1) = 8164168737599.0_R_P/31384184832000.0_R_P - self%b(2) = -122594813904112.0_R_P/31384184832000.0_R_P - self%b(3) = 859236476684231.0_R_P/31384184832000.0_R_P - self%b(4) = -3728807256577472.0_R_P/31384184832000.0_R_P - self%b(5) = 11205849753515179.0_R_P/31384184832000.0_R_P - self%b(6) = -24704503655607728.0_R_P/31384184832000.0_R_P - self%b(7) = 41280216336284259.0_R_P/31384184832000.0_R_P - self%b(8) = -53246738660646912.0_R_P/31384184832000.0_R_P - self%b(9) = 53471026659940509.0_R_P/31384184832000.0_R_P - self%b(10) = -41825269932507728.0_R_P/31384184832000.0_R_P - self%b(11) = 25298910337081429.0_R_P/31384184832000.0_R_P - self%b(12) = -11643637530577472.0_R_P/31384184832000.0_R_P - self%b(13) = 3966421670215481.0_R_P/31384184832000.0_R_P - self%b(14) = -960122866404112.0_R_P/31384184832000.0_R_P - self%b(15) = 173233498598849.0_R_P/31384184832000.0_R_P - case('adams_bashforth_16') - self%steps = 16 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P - self%b(1) = -16088129229375.0_R_P/62768369664000.0_R_P - self%b(2) = 257650275915823.0_R_P/62768369664000.0_R_P - self%b(3) = -1934443196892599.0_R_P/62768369664000.0_R_P - self%b(4) = 9038571752734087.0_R_P/62768369664000.0_R_P - self%b(5) = -29417910911251819.0_R_P/62768369664000.0_R_P - self%b(6) = 70724351582843483.0_R_P/62768369664000.0_R_P - self%b(7) = -129930094104237331.0_R_P/62768369664000.0_R_P - self%b(8) = 186087544263596643.0_R_P/62768369664000.0_R_P - self%b(9) = -210020588912321949.0_R_P/62768369664000.0_R_P - self%b(10) = 187463140112902893.0_R_P/62768369664000.0_R_P - self%b(11) = -131963191940828581.0_R_P/62768369664000.0_R_P - self%b(12) = 72558117072259733.0_R_P/62768369664000.0_R_P - self%b(13) = -30607373860520569.0_R_P/62768369664000.0_R_P - self%b(14) = 9622096909515337.0_R_P/62768369664000.0_R_P - self%b(15) = -2161567671248849.0_R_P/62768369664000.0_R_P - self%b(16) = 362555126427073.0_R_P/62768369664000.0_R_P - endselect - else - call self%trigger_error(error=ERROR_UNSUPPORTED_SCHEME, & - error_message='"'//trim(adjustl(scheme))//'" unsupported scheme', & - is_severe=.true.) - endif - endsubroutine initialize - - subroutine integrate(self, U, previous, Dt, t, autoupdate) - !< Integrate field with Adams-Bashforth class scheme. - class(integrator_adams_bashforth), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand field. - real(R_P), intent(in) :: Dt !< Time steps. - real(R_P), intent(in) :: t(:) !< Times. - logical, optional, intent(in) :: autoupdate !< Perform cyclic autoupdate of previous time steps. - logical :: autoupdate_ !< Perform cyclic autoupdate of previous time steps, dummy var. - integer(I_P) :: s !< Steps counter. - - autoupdate_ = .true. ; if (present(autoupdate)) autoupdate_ = autoupdate - do s=1, self%steps - U = U + (previous(s)%t(t=t(s)) * (Dt * self%b(s))) - enddo - if (autoupdate_) call self%update_previous(U=U, previous=previous) - endsubroutine integrate - - subroutine integrate_fast(self, U, previous, buffer, Dt, t, autoupdate) - !< Integrate field with Adams-Bashforth class scheme. - class(integrator_adams_bashforth), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand field. - class(integrand_object), intent(inout) :: buffer !< Temporary buffer for doing fast operation. - real(R_P), intent(in) :: Dt !< Time steps. - real(R_P), intent(in) :: t(:) !< Times. - logical, optional, intent(in) :: autoupdate !< Perform cyclic autoupdate of previous time steps. - logical :: autoupdate_ !< Perform cyclic autoupdate of previous time steps, dummy var. - integer(I_P) :: s !< Steps counter. - - autoupdate_ = .true. ; if (present(autoupdate)) autoupdate_ = autoupdate - do s=1, self%steps - buffer = previous(s) - call buffer%t_fast(t=t(s)) - call buffer%multiply_fast(lhs=buffer, rhs=Dt * self%b(s)) - call U%add_fast(lhs=U, rhs=buffer) - enddo - if (autoupdate_) call self%update_previous(U=U, previous=previous) - endsubroutine integrate_fast - - subroutine update_previous(self, U, previous) - !< Cyclic update previous time steps. - class(integrator_adams_bashforth), intent(in) :: self !< Integrator. - class(integrand_object), intent(in) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand field. - integer(I_P) :: s !< Steps counter. - - do s=1, self%steps - 1 - previous(s) = previous(s + 1) - enddo - previous(self%steps) = U - endsubroutine update_previous + ! deferred methods + pure function class_name(self) + !< Return the class name of schemes. + class(integrator_adams_bashforth), intent(in) :: self !< Integrator. + character(len=99) :: class_name !< Class name. + + class_name = trim(adjustl(class_name_)) + endfunction class_name + + elemental function has_fast_mode(self) + !< Return .true. if the integrator class has *fast mode* integrate. + class(integrator_adams_bashforth), intent(in) :: self !< Integrator. + logical :: has_fast_mode !< Inquire result. + + has_fast_mode = has_fast_mode_ + endfunction has_fast_mode + + pure subroutine integr_assign_integr(lhs, rhs) + !< Operator `=`. + class(integrator_adams_bashforth), intent(inout) :: lhs !< Left hand side. + class(integrator_object), intent(in) :: rhs !< Right hand side. + + call lhs%assign_multistep(rhs=rhs) + select type(rhs) + class is (integrator_adams_bashforth) + if (allocated(rhs%b)) lhs%b = rhs%b + endselect + endsubroutine integr_assign_integr + + subroutine integrate(self, U, Dt, t) + !< Integrate field with Adams-Bashforth class scheme. + class(integrator_adams_bashforth), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + integer(I_P) :: s !< Steps counter. + + do s=1, self%steps + U = U + (self%previous(s)%t(t=self%t(s)) * (Dt * self%b(s))) + enddo + if (self%autoupdate) call self%update_previous(U=U, previous=self%previous, Dt=Dt, t=t, previous_t=self%t) + endsubroutine integrate + + subroutine integrate_fast(self, U, Dt, t) + !< Integrate field with Adams-Bashforth class scheme, fast mode. + class(integrator_adams_bashforth), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + integer(I_P) :: s !< Steps counter. + + do s=1, self%steps + self%buffer = self%previous(s) + call self%buffer%t_fast(t=self%t(s)) + call self%buffer%multiply_fast(lhs=self%buffer, rhs=Dt * self%b(s)) + call U%add_fast(lhs=U, rhs=self%buffer) + enddo + if (self%autoupdate) call self%update_previous(U=U, previous=self%previous, Dt=Dt, t=t, previous_t=self%t) + endsubroutine integrate_fast + + elemental function is_supported(self, scheme) + !< Return .true. if the integrator class support the given scheme. + class(integrator_adams_bashforth), intent(in) :: self !< Integrator. + character(*), intent(in) :: scheme !< Selected scheme. + logical :: is_supported !< Inquire result. + integer(I_P) :: s !< Counter. + + is_supported = .false. + do s=lbound(supported_schemes_, dim=1), ubound(supported_schemes_, dim=1) + if (trim(adjustl(scheme)) == trim(adjustl(supported_schemes_(s)))) then + is_supported = .true. + return + endif + enddo + endfunction is_supported + + pure function supported_schemes(self) result(schemes) + !< Return the list of supported schemes. + class(integrator_adams_bashforth), intent(in) :: self !< Integrator. + character(len=99), allocatable :: schemes(:) !< Queried scheme. + + allocate(schemes(lbound(supported_schemes_, dim=1):ubound(supported_schemes_, dim=1))) + schemes = supported_schemes_ + endfunction supported_schemes + + ! public methods + elemental subroutine destroy(self) + !< Destroy the integrator. + class(integrator_adams_bashforth), intent(inout) :: self !< Integrator. + + call self%destroy_multistep + if (allocated(self%b)) deallocate(self%b) + endsubroutine destroy + + subroutine initialize(self, scheme, autoupdate, U, stop_on_fail) + !< Create the actual Adams-Bashforth integrator: initialize the *b* coefficients. + !< + !< @note If the integrator is initialized with a bad (unsupported) number of required time steps the initialization fails and + !< the integrator error status is updated consistently for external-provided errors handling. + class(integrator_adams_bashforth), intent(inout) :: self !< Integrator. + character(*), intent(in) :: scheme !< Selected scheme. + logical, intent(in), optional :: autoupdate !< Enable cyclic autoupdate of previous time steps. + class(integrand_object), intent(in), optional :: U !< Integrand molding prototype. + logical, intent(in), optional :: stop_on_fail !< Stop execution if initialization fail. + + if (self%is_supported(scheme=scheme)) then + call self%destroy + self%description_ = trim(adjustl(scheme)) + select case(trim(adjustl(scheme))) + case('adams_bashforth_1') + self%steps = 1 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P + self%b(1) = 1.0_R_P + case('adams_bashforth_2') + self%steps = 2 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P + self%b(1) = -1.0_R_P/2.0_R_P + self%b(2) = 3.0_R_P/2.0_R_P + case('adams_bashforth_3') + self%steps = 3 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P + self%b(1) = 5.0_R_P/12.0_R_P + self%b(2) = -16.0_R_P/12.0_R_P + self%b(3) = 23.0_R_P/12.0_R_P + case('adams_bashforth_4') + self%steps = 4 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P + self%b(1) = -9.0_R_P/24.0_R_P + self%b(2) = 37.0_R_P/24.0_R_P + self%b(3) = -59.0_R_P/24.0_R_P + self%b(4) = 55.0_R_P/24.0_R_P + case('adams_bashforth_5') + self%steps = 5 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P + self%b(1) = 251.0_R_P/720.0_R_P + self%b(2) = -1274.0_R_P/720.0_R_P + self%b(3) = 2616.0_R_P/720.0_R_P + self%b(4) = -2774.0_R_P/720.0_R_P + self%b(5) = 1901.0_R_P/720.0_R_P + case('adams_bashforth_6') + self%steps = 6 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P + self%b(1) = -475.0_R_P/1440.0_R_P + self%b(2) = 2877.0_R_P/1440.0_R_P + self%b(3) = -7298.0_R_P/1440.0_R_P + self%b(4) = 9982.0_R_P/1440.0_R_P + self%b(5) = -7923.0_R_P/1440.0_R_P + self%b(6) = 4277.0_R_P/1440.0_R_P + case('adams_bashforth_7') + self%steps = 7 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P + self%b(1) = 19087.0_R_P/60480.0_R_P + self%b(2) = -134472.0_R_P/60480.0_R_P + self%b(3) = 407139.0_R_P/60480.0_R_P + self%b(4) = -688256.0_R_P/60480.0_R_P + self%b(5) = 705549.0_R_P/60480.0_R_P + self%b(6) = -447288.0_R_P/60480.0_R_P + self%b(7) = 198721.0_R_P/60480.0_R_P + case('adams_bashforth_8') + self%steps = 8 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P + self%b(1) = -36799.0_R_P/120960.0_R_P + self%b(2) = 295767.0_R_P/120960.0_R_P + self%b(3) = -1041723.0_R_P/120960.0_R_P + self%b(4) = 2102243.0_R_P/120960.0_R_P + self%b(5) = -2664477.0_R_P/120960.0_R_P + self%b(6) = 2183877.0_R_P/120960.0_R_P + self%b(7) = -1152169.0_R_P/120960.0_R_P + self%b(8) = 434241.0_R_P/120960.0_R_P + case('adams_bashforth_9') + self%steps = 9 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P + self%b(1) = 1070017.0_R_P/3628800.0_R_P + self%b(2) = -9664106.0_R_P/3628800.0_R_P + self%b(3) = 38833486.0_R_P/3628800.0_R_P + self%b(4) = -91172642.0_R_P/3628800.0_R_P + self%b(5) = 137968480.0_R_P/3628800.0_R_P + self%b(6) = -139855262.0_R_P/3628800.0_R_P + self%b(7) = 95476786.0_R_P/3628800.0_R_P + self%b(8) = -43125206.0_R_P/3628800.0_R_P + self%b(9) = 14097247.0_R_P/3628800.0_R_P + case('adams_bashforth_10') + self%steps = 10 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P + self%b(1) = -2082753.0_R_P/7257600.0_R_P + self%b(2) = 20884811.0_R_P/7257600.0_R_P + self%b(3) = -94307320.0_R_P/7257600.0_R_P + self%b(4) = 252618224.0_R_P/7257600.0_R_P + self%b(5) = -444772162.0_R_P/7257600.0_R_P + self%b(6) = 538363838.0_R_P/7257600.0_R_P + self%b(7) = -454661776.0_R_P/7257600.0_R_P + self%b(8) = 265932680.0_R_P/7257600.0_R_P + self%b(9) = -104995189.0_R_P/7257600.0_R_P + self%b(10) = 30277247.0_R_P/7257600.0_R_P + case('adams_bashforth_11') + self%steps = 11 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P + self%b(1) = 134211265.0_R_P/479001600.0_R_P + self%b(2) = -1479574348.0_R_P/479001600.0_R_P + self%b(3) = 7417904451.0_R_P/479001600.0_R_P + self%b(4) = -22329634920.0_R_P/479001600.0_R_P + self%b(5) = 44857168434.0_R_P/479001600.0_R_P + self%b(6) = -63176201472.0_R_P/479001600.0_R_P + self%b(7) = 63716378958.0_R_P/479001600.0_R_P + self%b(8) = -46113029016.0_R_P/479001600.0_R_P + self%b(9) = 23591063805.0_R_P/479001600.0_R_P + self%b(10) = -8271795124.0_R_P/479001600.0_R_P + self%b(11) = 2132509567.0_R_P/479001600.0_R_P + case('adams_bashforth_12') + self%steps = 12 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P + self%b(1) = -262747265.0_R_P/958003200.0_R_P + self%b(2) = 3158642445.0_R_P/958003200.0_R_P + self%b(3) = -17410248271.0_R_P/958003200.0_R_P + self%b(4) = 58189107627.0_R_P/958003200.0_R_P + self%b(5) = -131365867290.0_R_P/958003200.0_R_P + self%b(6) = 211103573298.0_R_P/958003200.0_R_P + self%b(7) = -247741639374.0_R_P/958003200.0_R_P + self%b(8) = 214139355366.0_R_P/958003200.0_R_P + self%b(9) = -135579356757.0_R_P/958003200.0_R_P + self%b(10) = 61633227185.0_R_P/958003200.0_R_P + self%b(11) = -19433810163.0_R_P/958003200.0_R_P + self%b(12) = 4527766399.0_R_P/958003200.0_R_P + case('adams_bashforth_13') + self%steps = 13 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P + self%b(1) = 703604254357.0_R_P/2615348736000.0_R_P + self%b(2) = -9160551085734.0_R_P/2615348736000.0_R_P + self%b(3) = 55060974662412.0_R_P/2615348736000.0_R_P + self%b(4) = -202322913738370.0_R_P/2615348736000.0_R_P + self%b(5) = 507140369728425.0_R_P/2615348736000.0_R_P + self%b(6) = -915883387152444.0_R_P/2615348736000.0_R_P + self%b(7) = 1226443086129408.0_R_P/2615348736000.0_R_P + self%b(8) = -1233589244941764.0_R_P/2615348736000.0_R_P + self%b(9) = 932884546055895.0_R_P/2615348736000.0_R_P + self%b(10) = -524924579905150.0_R_P/2615348736000.0_R_P + self%b(11) = 214696591002612.0_R_P/2615348736000.0_R_P + self%b(12) = -61497552797274.0_R_P/2615348736000.0_R_P + self%b(13) = 13064406523627.0_R_P/2615348736000.0_R_P + case('adams_bashforth_14') + self%steps = 14 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P + self%b(1) = -1382741929621.0_R_P/5230697472000.0_R_P + self%b(2) = 19382853593787.0_R_P/5230697472000.0_R_P + self%b(3) = -126174972681906.0_R_P/5230697472000.0_R_P + self%b(4) = 505586141196430.0_R_P/5230697472000.0_R_P + self%b(5) = -1393306307155755.0_R_P/5230697472000.0_R_P + self%b(6) = 2793869602879077.0_R_P/5230697472000.0_R_P + self%b(7) = -4204551925534524.0_R_P/5230697472000.0_R_P + self%b(8) = 4825671323488452.0_R_P/5230697472000.0_R_P + self%b(9) = -4246767353305755.0_R_P/5230697472000.0_R_P + self%b(10) = 2854429571790805.0_R_P/5230697472000.0_R_P + self%b(11) = -1445313351681906.0_R_P/5230697472000.0_R_P + self%b(12) = 537247052515662.0_R_P/5230697472000.0_R_P + self%b(13) = -140970750679621.0_R_P/5230697472000.0_R_P + self%b(14) = 27511554976875.0_R_P/5230697472000.0_R_P + case('adams_bashforth_15') + self%steps = 15 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P + self%b(1) = 8164168737599.0_R_P/31384184832000.0_R_P + self%b(2) = -122594813904112.0_R_P/31384184832000.0_R_P + self%b(3) = 859236476684231.0_R_P/31384184832000.0_R_P + self%b(4) = -3728807256577472.0_R_P/31384184832000.0_R_P + self%b(5) = 11205849753515179.0_R_P/31384184832000.0_R_P + self%b(6) = -24704503655607728.0_R_P/31384184832000.0_R_P + self%b(7) = 41280216336284259.0_R_P/31384184832000.0_R_P + self%b(8) = -53246738660646912.0_R_P/31384184832000.0_R_P + self%b(9) = 53471026659940509.0_R_P/31384184832000.0_R_P + self%b(10) = -41825269932507728.0_R_P/31384184832000.0_R_P + self%b(11) = 25298910337081429.0_R_P/31384184832000.0_R_P + self%b(12) = -11643637530577472.0_R_P/31384184832000.0_R_P + self%b(13) = 3966421670215481.0_R_P/31384184832000.0_R_P + self%b(14) = -960122866404112.0_R_P/31384184832000.0_R_P + self%b(15) = 173233498598849.0_R_P/31384184832000.0_R_P + case('adams_bashforth_16') + self%steps = 16 ; allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P + self%b(1) = -16088129229375.0_R_P/62768369664000.0_R_P + self%b(2) = 257650275915823.0_R_P/62768369664000.0_R_P + self%b(3) = -1934443196892599.0_R_P/62768369664000.0_R_P + self%b(4) = 9038571752734087.0_R_P/62768369664000.0_R_P + self%b(5) = -29417910911251819.0_R_P/62768369664000.0_R_P + self%b(6) = 70724351582843483.0_R_P/62768369664000.0_R_P + self%b(7) = -129930094104237331.0_R_P/62768369664000.0_R_P + self%b(8) = 186087544263596643.0_R_P/62768369664000.0_R_P + self%b(9) = -210020588912321949.0_R_P/62768369664000.0_R_P + self%b(10) = 187463140112902893.0_R_P/62768369664000.0_R_P + self%b(11) = -131963191940828581.0_R_P/62768369664000.0_R_P + self%b(12) = 72558117072259733.0_R_P/62768369664000.0_R_P + self%b(13) = -30607373860520569.0_R_P/62768369664000.0_R_P + self%b(14) = 9622096909515337.0_R_P/62768369664000.0_R_P + self%b(15) = -2161567671248849.0_R_P/62768369664000.0_R_P + self%b(16) = 362555126427073.0_R_P/62768369664000.0_R_P + endselect + self%autoupdate = .true. ; if (present(autoupdate)) self%autoupdate = autoupdate + self%registers = self%steps + if (present(U)) call self%allocate_integrand_members(U=U) + else + call self%trigger_error(error=ERROR_UNSUPPORTED_SCHEME, & + error_message='"'//trim(adjustl(scheme))//'" unsupported scheme', & + is_severe=stop_on_fail) + endif + endsubroutine initialize endmodule foodie_integrator_adams_bashforth diff --git a/src/lib/foodie_integrator_adams_bashforth_moulton.f90 b/src/lib/foodie_integrator_adams_bashforth_moulton.f90 index 43cad9db..297ba49c 100644 --- a/src/lib/foodie_integrator_adams_bashforth_moulton.f90 +++ b/src/lib/foodie_integrator_adams_bashforth_moulton.f90 @@ -80,6 +80,7 @@ module foodie_integrator_adams_bashforth_moulton use foodie_integrand_object, only : integrand_object use foodie_integrator_adams_bashforth, only : integrator_adams_bashforth use foodie_integrator_adams_moulton, only : integrator_adams_moulton +use foodie_integrator_multistep_object, only : integrator_multistep_object use foodie_integrator_object, only : integrator_object use penf, only : I_P, R_P @@ -106,35 +107,30 @@ module foodie_integrator_adams_bashforth_moulton trim(class_name_)//'_16'] !< List of supported schemes. logical, parameter :: has_fast_mode_=.true. !< Flag to check if integrator provides *fast mode* integrate. -logical, parameter :: is_multistage_=.false. !< Flag to check if integrator is multistage. -logical, parameter :: is_multistep_=.true. !< Flag to check if integrator is multistep. -type, extends(integrator_object) :: integrator_adams_bashforth_moulton +type, extends(integrator_multistep_object) :: integrator_adams_bashforth_moulton !< FOODIE integrator: provide an explicit class of Adams-Bashforth-Moulton multi-step schemes, from 1st to 4rd order accurate. !< !< @note The integrator must be created or initialized (predictor and corrector schemes selection) before used. + !< + !< @note The time steps `Dt(1:steps)` passed to the integrate methods must be identical: this integrator supports only + !< fixed time steps. private - integer(I_P) :: steps=0 !< Number of time steps. type(integrator_adams_bashforth) :: predictor !< Predictor solver. type(integrator_adams_moulton) :: corrector !< Corrector solver. contains ! deferred methods procedure, pass(self) :: class_name !< Return the class name of schemes. - procedure, pass(self) :: description !< Return pretty-printed object description. procedure, pass(self) :: has_fast_mode !< Return .true. if the integrator class has *fast mode* integrate. procedure, pass(lhs) :: integr_assign_integr !< Operator `=`. - procedure, pass(self) :: is_multistage !< Return .true. for multistage integrator. - procedure, pass(self) :: is_multistep !< Return .true. for multistep integrator. + procedure, pass(self) :: integrate !< Integrate integrand field. + procedure, pass(self) :: integrate_fast !< Integrate integrand field. procedure, pass(self) :: is_supported !< Return .true. if the integrator class support the given scheme. - procedure, pass(self) :: stages_number !< Return number of stages used. - procedure, pass(self) :: steps_number !< Return number of steps used. procedure, pass(self) :: supported_schemes !< Return the list of supported schemes. ! public methods - procedure, pass(self) :: destroy !< Destroy the integrator. - procedure, pass(self) :: initialize !< Initialize (create) the integrator. - procedure, pass(self) :: integrate !< Integrate integrand field. - procedure, pass(self) :: integrate_fast !< Integrate integrand field. - procedure, pass(self) :: scheme_number !< Return the scheme number in the list of supported schemes. + procedure, pass(self) :: destroy !< Destroy the integrator. + procedure, pass(self) :: initialize !< Initialize (create) the integrator. + procedure, pass(self) :: scheme_number !< Return the scheme number in the list of supported schemes. endtype integrator_adams_bashforth_moulton contains @@ -147,25 +143,6 @@ pure function class_name(self) class_name = trim(adjustl(class_name_)) endfunction class_name - pure function description(self, prefix) result(desc) - !< Return a pretty-formatted object description. - class(integrator_adams_bashforth_moulton), intent(in) :: self !< Integrator. - character(*), intent(in), optional :: prefix !< Prefixing string. - character(len=:), allocatable :: desc !< Description. - character(len=:), allocatable :: prefix_ !< Prefixing string, local variable. - character(len=1), parameter :: NL=new_line('a') !< New line character. - integer(I_P) :: s !< Counter. - - prefix_ = '' ; if (present(prefix)) prefix_ = prefix - desc = '' - desc = desc//prefix_//'Adams-Bashforth-Moulton multi-step (predictor-corrector) schemes class'//NL - desc = desc//prefix_//' Supported schemes:'//NL - do s=lbound(supported_schemes_, dim=1), ubound(supported_schemes_, dim=1) - 1 - desc = desc//prefix_//' + '//supported_schemes_(s)//NL - enddo - desc = desc//prefix_//' + '//supported_schemes_(ubound(supported_schemes_, dim=1)) - endfunction description - elemental function has_fast_mode(self) !< Return .true. if the integrator class has *fast mode* integrate. class(integrator_adams_bashforth_moulton), intent(in) :: self !< Integrator. @@ -179,7 +156,7 @@ pure subroutine integr_assign_integr(lhs, rhs) class(integrator_adams_bashforth_moulton), intent(inout) :: lhs !< Left hand side. class(integrator_object), intent(in) :: rhs !< Right hand side. - call lhs%assign_abstract(rhs=rhs) + call lhs%assign_multistep(rhs=rhs) select type(rhs) class is (integrator_adams_bashforth_moulton) lhs%steps = rhs%steps @@ -188,21 +165,53 @@ pure subroutine integr_assign_integr(lhs, rhs) endselect endsubroutine integr_assign_integr - elemental function is_multistage(self) - !< Return .true. for multistage integrator. - class(integrator_adams_bashforth_moulton), intent(in) :: self !< Integrator. - logical :: is_multistage !< Inquire result. - - is_multistage = is_multistage_ - endfunction is_multistage + subroutine integrate(self, U, Dt, t) + !< Integrate field with Adams-Bashforth-Moulton class scheme. + class(integrator_adams_bashforth_moulton), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time steps. + real(R_P), intent(in) :: t !< Times. + integer(I_P) :: s !< Step counter. + + do s=1, self%steps + self%predictor%previous(s) = self%previous(s) + self%predictor%t(s) = self%t(s) + self%predictor%Dt(s) = self%Dt(s) + enddo + do s=1, self%steps - 1 + self%corrector%previous(s) = self%predictor%previous(s+1) + self%corrector%t(s) = self%predictor%t(s+1) + self%corrector%Dt(s) = self%predictor%Dt(s+1) + enddo + call self%predictor%integrate(U=U, Dt=Dt, t=t) + call self%corrector%integrate(U=U, Dt=Dt, t=t) + if (self%autoupdate) & + call self%update_previous(U=U, previous=self%previous, Dt=Dt, t=t, previous_t=self%t) + endsubroutine integrate - elemental function is_multistep(self) - !< Return .true. for multistage integrator. - class(integrator_adams_bashforth_moulton), intent(in) :: self !< Integrator. - logical :: is_multistep !< Inquire result. + subroutine integrate_fast(self, U, Dt, t) + !< Integrate field with Adams-Bashforth-Moulton class scheme, fast mode. + class(integrator_adams_bashforth_moulton), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time steps. + real(R_P), intent(in) :: t !< Times. + integer(I_P) :: s !< Step counter. - is_multistep = is_multistep_ - endfunction is_multistep + do s=1, self%steps + self%predictor%previous(s) = self%previous(s) + enddo + self%predictor%t(:) = self%t(1:self%steps) + self%predictor%Dt(:) = self%Dt(1:self%steps) + do s=1, self%steps - 1 + self%corrector%previous(s) = self%previous(s+1) + enddo + self%corrector%t(:) = self%t(2:self%steps) + self%corrector%Dt(:) = self%Dt(2:self%steps) + call self%predictor%integrate_fast(U=U, Dt=Dt, t=t) + call self%corrector%integrate_fast(U=U, Dt=Dt, t=t) + if (self%autoupdate) & + call self%update_previous(U=U, previous=self%previous(1:self%steps), Dt=Dt, t=t, previous_t=self%t(1:self%steps)) + endsubroutine integrate_fast elemental function is_supported(self, scheme) !< Return .true. if the integrator class support the given scheme. @@ -220,22 +229,6 @@ elemental function is_supported(self, scheme) enddo endfunction is_supported - elemental function stages_number(self) - !< Return number of stages used. - class(integrator_adams_bashforth_moulton), intent(in) :: self !< Integrator. - integer(I_P) :: stages_number !< Number of stages used. - - stages_number = 0 - endfunction stages_number - - elemental function steps_number(self) - !< Return number of steps used. - class(integrator_adams_bashforth_moulton), intent(in) :: self !< Integrator. - integer(I_P) :: steps_number !< Number of steps used. - - steps_number = self%steps - endfunction steps_number - pure function supported_schemes(self) result(schemes) !< Return the list of supported schemes. class(integrator_adams_bashforth_moulton), intent(in) :: self !< Integrator. @@ -250,65 +243,43 @@ elemental subroutine destroy(self) !< Destroy the integrator. class(integrator_adams_bashforth_moulton), intent(inout) :: self !< Integrator. - call self%destroy_abstract - self%steps = 0 + call self%destroy_multistep call self%predictor%destroy call self%corrector%destroy endsubroutine destroy - subroutine initialize(self, scheme) + subroutine initialize(self, scheme, iterations, autoupdate, U, stop_on_fail) !< Create the actual Adams-Bashforth-Moulton integrator: initialize the *b* coefficients. - class(integrator_adams_bashforth_moulton), intent(inout) :: self !< Integrator. - character(*), intent(in) :: scheme !< Selected scheme. - character(len=99), allocatable :: schemes_ab(:) !< Adams-Bashforth schemes. - character(len=99), allocatable :: schemes_am(:) !< Adams-Moulton schemes. - integer(I_P) :: scheme_number_ !< Scheme number in the list of supported schemes. + class(integrator_adams_bashforth_moulton), intent(inout) :: self !< Integrator. + character(*), intent(in) :: scheme !< Selected scheme. + integer(I_P), intent(in), optional :: iterations !< Implicit iterations. + logical, intent(in), optional :: autoupdate !< Enable cyclic autoupdate of previous time steps. + class(integrand_object), intent(in), optional :: U !< Integrand molding prototype. + logical, intent(in), optional :: stop_on_fail !< Stop execution if initialization fail. + character(len=99), allocatable :: schemes_ab(:) !< Adams-Bashforth schemes. + character(len=99), allocatable :: schemes_am(:) !< Adams-Moulton schemes. + integer(I_P) :: scheme_number_ !< Scheme number in the list of supported schemes. if (self%is_supported(scheme=scheme)) then call self%destroy + self%description_ = trim(adjustl(scheme)) scheme_number_ = self%scheme_number(scheme=scheme) schemes_ab = self%predictor%supported_schemes() schemes_am = self%corrector%supported_schemes() - call self%predictor%initialize(scheme=schemes_ab(scheme_number_)) - call self%corrector%initialize(scheme=schemes_am(scheme_number_)) + self%autoupdate = .true. ; if (present(autoupdate)) self%autoupdate = autoupdate + self%iterations = 1 ; if (present(iterations)) self%iterations = iterations + call self%predictor%initialize(scheme=schemes_ab(scheme_number_), U=U, autoupdate=.false.) + call self%corrector%initialize(scheme=schemes_am(scheme_number_), U=U, iterations=self%iterations, autoupdate=.false.) self%steps = self%predictor%steps_number() + self%registers = self%steps + if (present(U)) call self%allocate_integrand_members(U=U) else call self%trigger_error(error=ERROR_UNSUPPORTED_SCHEME, & error_message='"'//trim(adjustl(scheme))//'" unsupported scheme', & - is_severe=.true.) + is_severe=stop_on_fail) endif endsubroutine initialize - subroutine integrate(self, U, previous, Dt, t, iterations) - !< Integrate field with Adams-Bashforth-Moulton class scheme. - class(integrator_adams_bashforth_moulton), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand. - real(R_P), intent(in) :: Dt !< Time steps. - real(R_P), intent(in) :: t(:) !< Times. - integer(I_P), intent(in), optional :: iterations !< Fixed point iterations of AM scheme. - - call self%predictor%integrate(U=U, previous=previous, Dt=Dt, t=t, autoupdate=.false.) - call self%corrector%integrate(U=U, previous=previous(2:), Dt=Dt, t=t, iterations=iterations, autoupdate=.false.) - call self%predictor%update_previous(U=U, previous=previous) - endsubroutine integrate - - subroutine integrate_fast(self, U, previous, buffer, Dt, t, iterations) - !< Integrate field with Adams-Bashforth-Moulton class scheme, fast mode. - class(integrator_adams_bashforth_moulton), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand. - class(integrand_object), intent(inout) :: buffer !< Temporary buffer for doing fast operation. - real(R_P), intent(in) :: Dt !< Time steps. - real(R_P), intent(in) :: t(:) !< Times. - integer(I_P), intent(in), optional :: iterations !< Fixed point iterations of AM scheme. - - call self%predictor%integrate_fast(U=U, previous=previous, buffer=buffer, Dt=Dt, t=t, autoupdate=.false.) - call self%corrector%integrate_fast(U=U, previous=previous(2:), buffer=buffer, Dt=Dt, t=t, iterations=iterations, & - autoupdate=.false.) - call self%predictor%update_previous(U=U, previous=previous) - endsubroutine integrate_fast - elemental function scheme_number(self, scheme) !< Return the scheme number in the list of supported schemes. class(integrator_adams_bashforth_moulton), intent(in) :: self !< Integrator. diff --git a/src/lib/foodie_integrator_adams_moulton.f90 b/src/lib/foodie_integrator_adams_moulton.f90 index 88b67ba0..62b0e73c 100644 --- a/src/lib/foodie_integrator_adams_moulton.f90 +++ b/src/lib/foodie_integrator_adams_moulton.f90 @@ -29,6 +29,7 @@ module foodie_integrator_adams_moulton use foodie_error_codes, only : ERROR_UNSUPPORTED_SCHEME use foodie_integrand_object, only : integrand_object +use foodie_integrator_multistep_object, only : integrator_multistep_object use foodie_integrator_object, only : integrator_object use penf, only : I_P, R_P @@ -55,34 +56,28 @@ module foodie_integrator_adams_moulton trim(class_name_)//'_15'] !< List of supported schemes. logical, parameter :: has_fast_mode_=.true. !< Flag to check if integrator provides *fast mode* integrate. -logical, parameter :: is_multistage_=.false. !< Flag to check if integrator is multistage. -logical, parameter :: is_multistep_=.true. !< Flag to check if integrator is multistep. -type, extends(integrator_object) :: integrator_adams_moulton +type, extends(integrator_multistep_object) :: integrator_adams_moulton !< FOODIE integrator: provide an explicit class of Adams-Moulton multi-step schemes, from 1st to 16th order accurate. !< !< @note The integrator must be created or initialized (initialize the *b* coefficients) before used. + !< + !< @note The time steps `Dt(1:steps)` passed to the integrate methods must be identical: this integrator supports only + !< fixed time steps. private - integer(I_P) :: steps=-1 !< Number of time steps. - real(R_P), allocatable :: b(:) !< \(b\) coefficients. + real(R_P), allocatable :: b(:) !< \(b\) coefficients. contains ! deferred methods procedure, pass(self) :: class_name !< Return the class name of schemes. - procedure, pass(self) :: description !< Return pretty-printed object description. procedure, pass(lhs) :: integr_assign_integr !< Operator `=`. procedure, pass(self) :: has_fast_mode !< Return .true. if the integrator class has *fast mode* integrate. - procedure, pass(self) :: is_multistage !< Return .true. for multistage integrator. - procedure, pass(self) :: is_multistep !< Return .true. for multistep integrator. + procedure, pass(self) :: integrate !< Integrate integrand field. + procedure, pass(self) :: integrate_fast !< Integrate integrand field, fast mode. procedure, pass(self) :: is_supported !< Return .true. if the integrator class support the given scheme. - procedure, pass(self) :: stages_number !< Return number of stages used. - procedure, pass(self) :: steps_number !< Return number of steps used. procedure, pass(self) :: supported_schemes !< Return the list of supported schemes. ! public methods - procedure, pass(self) :: destroy !< Destroy the integrator. - procedure, pass(self) :: initialize !< Initialize (create) the integrator. - procedure, pass(self) :: integrate !< Integrate integrand field. - procedure, pass(self) :: integrate_fast !< Integrate integrand field, fast mode. - procedure, pass(self) :: update_previous !< Cyclic update previous time steps. + procedure, pass(self) :: destroy !< Destroy the integrator. + procedure, pass(self) :: initialize !< Initialize (create) the integrator. endtype integrator_adams_moulton contains @@ -95,25 +90,6 @@ pure function class_name(self) class_name = trim(adjustl(class_name_)) endfunction class_name - pure function description(self, prefix) result(desc) - !< Return a pretty-formatted object description. - class(integrator_adams_moulton), intent(in) :: self !< Integrator. - character(*), intent(in), optional :: prefix !< Prefixing string. - character(len=:), allocatable :: desc !< Description. - character(len=:), allocatable :: prefix_ !< Prefixing string, local variable. - character(len=1), parameter :: NL=new_line('a') !< New line character. - integer(I_P) :: s !< Counter. - - prefix_ = '' ; if (present(prefix)) prefix_ = prefix - desc = '' - desc = desc//prefix_//'Adams-Moulton multi-step schemes class'//NL - desc = desc//prefix_//' Supported schemes:'//NL - do s=lbound(supported_schemes_, dim=1), ubound(supported_schemes_, dim=1) - 1 - desc = desc//prefix_//' + '//supported_schemes_(s)//NL - enddo - desc = desc//prefix_//' + '//supported_schemes_(ubound(supported_schemes_, dim=1)) - endfunction description - elemental function has_fast_mode(self) !< Return .true. if the integrator class has *fast mode* integrate. class(integrator_adams_moulton), intent(in) :: self !< Integrator. @@ -127,7 +103,7 @@ pure subroutine integr_assign_integr(lhs, rhs) class(integrator_adams_moulton), intent(inout) :: lhs !< Left hand side. class(integrator_object), intent(in) :: rhs !< Right hand side. - call lhs%assign_abstract(rhs=rhs) + call lhs%assign_multistep(rhs=rhs) select type(rhs) class is (integrator_adams_moulton) lhs%steps = rhs%steps @@ -135,21 +111,82 @@ pure subroutine integr_assign_integr(lhs, rhs) endselect endsubroutine integr_assign_integr - elemental function is_multistage(self) - !< Return .true. for multistage integrator. - class(integrator_adams_moulton), intent(in) :: self !< Integrator. - logical :: is_multistage !< Inquire result. - - is_multistage = is_multistage_ - endfunction is_multistage + subroutine integrate(self, U, Dt, t) + !< Integrate field with Adams-Moulton class scheme. + class(integrator_adams_moulton), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + class(integrand_object), allocatable :: delta !< Delta RHS for fixed point iterations. + integer(I_P) :: s !< Steps counter. + + if ( self%steps>0 ) then + if (self%iterations > 0) then ! perform fixed point iterations + allocate(delta, mold=U) + delta = self%previous(self%steps) + do s=0, self%steps - 1 + delta = delta + (self%previous(s+1)%t(t=self%t(s+1)) * (Dt * self%b(s))) + enddo + do s=1, self%iterations + U = delta + (U%t(t=self%t(self%steps) + Dt) * (Dt * self%b(self%steps))) + enddo + else + U = self%previous(self%steps) + (U%t(t=self%t(self%steps) + Dt) * (Dt * self%b(self%steps))) + do s=0, self%steps - 1 + U = U + (self%previous(s+1)%t(t=self%t(s+1)) * (Dt * self%b(s))) + enddo + endif + if (self%autoupdate) call self%update_previous(U=U, previous=self%previous, Dt=Dt, t=t, previous_t=self%t) + else + U = U + (U%t(t=t) * (Dt * self%b(0))) + endif + endsubroutine integrate - elemental function is_multistep(self) - !< Return .true. for multistage integrator. - class(integrator_adams_moulton), intent(in) :: self !< Integrator. - logical :: is_multistep !< Inquire result. + subroutine integrate_fast(self, U, Dt, t) + !< Integrate field with Adams-Moulton class scheme, fast mode. + class(integrator_adams_moulton), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + class(integrand_object), allocatable :: delta !< Delta RHS for fixed point iterations. + integer(I_P) :: s !< Steps counter. - is_multistep = is_multistep_ - endfunction is_multistep + if (self%steps>0) then + if (self%iterations > 0) then ! perform fixed point iterations + allocate(delta, mold=U) + delta = self%previous(self%steps) + do s=0, self%steps - 1 + self%buffer = self%previous(s+1) + call self%buffer%t_fast(t=self%t(s+1)) + call self%buffer%multiply_fast(lhs=self%buffer, rhs=Dt * self%b(s)) + call delta%add_fast(lhs=delta, rhs=self%buffer) + enddo + do s=1, self%iterations + self%buffer = U + call self%buffer%t_fast(t=self%t(self%steps) + Dt) + call self%buffer%multiply_fast(lhs=self%buffer, rhs=Dt * self%b(self%steps)) + call U%add_fast(lhs=delta, rhs=self%buffer) + enddo + else + self%buffer = U + call self%buffer%t_fast(t=self%t(self%steps) + Dt) + call self%buffer%multiply_fast(lhs=self%buffer, rhs=Dt * self%b(self%steps)) + call U%add_fast(lhs=self%previous(self%steps), rhs=self%buffer) + do s=0, self%steps - 1 + self%buffer = self%previous(s+1) + call self%buffer%t_fast(t=self%t(s+1)) + call self%buffer%multiply_fast(lhs=self%buffer, rhs=Dt * self%b(s)) + call U%add_fast(lhs=U, rhs=self%buffer) + enddo + endif + if (self%autoupdate) call self%update_previous(U=U, previous=self%previous, Dt=Dt, t=t, previous_t=self%t) + else + self%buffer = U + call self%buffer%t_fast(t=t) + call self%buffer%multiply_fast(lhs=self%buffer, rhs=Dt * self%b(0)) + call U%add_fast(lhs=U, rhs=self%buffer) + endif + endsubroutine integrate_fast elemental function is_supported(self, scheme) !< Return .true. if the integrator class support the given scheme. @@ -167,22 +204,6 @@ elemental function is_supported(self, scheme) enddo endfunction is_supported - elemental function stages_number(self) - !< Return number of stages used. - class(integrator_adams_moulton), intent(in) :: self !< Integrator. - integer(I_P) :: stages_number !< Number of stages used. - - stages_number = 0 - endfunction stages_number - - elemental function steps_number(self) - !< Return number of steps used. - class(integrator_adams_moulton), intent(in) :: self !< Integrator. - integer(I_P) :: steps_number !< Number of steps used. - - steps_number = self%steps - endfunction steps_number - pure function supported_schemes(self) result(schemes) !< Return the list of supported schemes. class(integrator_adams_moulton), intent(in) :: self !< Integrator. @@ -197,18 +218,22 @@ elemental subroutine destroy(self) !< Destroy the integrator. class(integrator_adams_moulton), intent(INOUT) :: self !< Integrator. - call self%destroy_abstract - self%steps = -1 + call self%destroy_multistep if (allocated(self%b)) deallocate(self%b) endsubroutine destroy - subroutine initialize(self, scheme) + subroutine initialize(self, scheme, iterations, autoupdate, U, stop_on_fail) !< Create the actual Adams-Moulton integrator: initialize the *b* coefficients. - class(integrator_adams_moulton), intent(inout) :: self !< Integrator. - character(*), intent(in) :: scheme !< Selected scheme. + class(integrator_adams_moulton), intent(inout) :: self !< Integrator. + character(*), intent(in) :: scheme !< Selected scheme. + integer(I_P), intent(in), optional :: iterations !< Implicit iterations. + logical, intent(in), optional :: autoupdate !< Enable cyclic autoupdate of previous time steps. + class(integrand_object), intent(in), optional :: U !< Integrand molding prototype. + logical, intent(in), optional :: stop_on_fail !< Stop execution if initialization fail. if (self%is_supported(scheme=scheme)) then - call self%destroy + call self%destroy + self%description_ = trim(adjustl(scheme)) select case(trim(adjustl(scheme))) case('adams_moulton_0') self%steps = 0 ; allocate(self%b(0:self%steps)) ; self%b = 0.0_R_P @@ -379,113 +404,14 @@ subroutine initialize(self, scheme) self%b(14) = 105145058757073.0_R_P/62768369664000.0_R_P self%b(15) = 16088129229375.0_R_P/62768369664000.0_R_P endselect + self%autoupdate = .true. ; if (present(autoupdate)) self%autoupdate = autoupdate + self%iterations = 1 ; if (present(iterations)) self%iterations = iterations + self%registers = self%steps + if (present(U)) call self%allocate_integrand_members(U=U) else call self%trigger_error(error=ERROR_UNSUPPORTED_SCHEME, & error_message='"'//trim(adjustl(scheme))//'" unsupported scheme', & - is_severe=.true.) + is_severe=stop_on_fail) endif endsubroutine initialize - - subroutine integrate(self, U, previous, Dt, t, iterations, autoupdate) - !< Integrate field with Adams-Moulton class scheme. - class(integrator_adams_moulton), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand field. - real(R_P), intent(in) :: Dt !< Time steps. - real(R_P), intent(in) :: t(:) !< Times. - integer(I_P), intent(in), optional :: iterations !< Fixed point iterations. - logical, intent(in), optional :: autoupdate !< Cyclic autoupdate of previous time steps flag. - logical :: autoupdate_ !< Cyclic autoupdate of previous time steps flag, dummy var. - class(integrand_object), allocatable :: delta !< Delta RHS for fixed point iterations. - integer(I_P) :: s !< Steps counter. - - autoupdate_ = .true. ; if (present(autoupdate)) autoupdate_ = autoupdate - if (self%steps>0) then - if (present(iterations)) then ! perform fixed point iterations - allocate(delta, mold=U) - delta = previous(self%steps) - do s=0, self%steps - 1 - delta = delta + (previous(s+1)%t(t=t(s+1)) * (Dt * self%b(s))) - enddo - do s=1, iterations - U = delta + (U%t(t=t(self%steps) + Dt) * (Dt * self%b(self%steps))) - enddo - else - U = previous(self%steps) + (U%t(t=t(self%steps) + Dt) * (Dt * self%b(self%steps))) - do s=0, self%steps - 1 - U = U + (previous(s+1)%t(t=t(s+1)) * (Dt * self%b(s))) - enddo - endif - if (autoupdate_) call self%update_previous(U=U, previous=previous) - else - U = U + (U%t(t=t(1)) * (Dt * self%b(0))) - endif - endsubroutine integrate - - subroutine integrate_fast(self, U, previous, buffer, Dt, t, iterations, autoupdate) - !< Integrate field with Adams-Moulton class scheme, fast mode. - class(integrator_adams_moulton), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand field. - class(integrand_object), intent(inout) :: buffer !< Temporary buffer for doing fast operation. - real(R_P), intent(in) :: Dt !< Time steps. - real(R_P), intent(in) :: t(:) !< Times. - integer(I_P), intent(in), optional :: iterations !< Fixed point iterations. - logical, intent(in), optional :: autoupdate !< Cyclic autoupdate of previous time steps flag. - logical :: autoupdate_ !< Cyclic autoupdate of previous time steps flag, dummy var. - class(integrand_object), allocatable :: delta !< Delta RHS for fixed point iterations. - integer(I_P) :: s !< Steps counter. - - autoupdate_ = .true. ; if (present(autoupdate)) autoupdate_ = autoupdate - if (self%steps>0) then - if (present(iterations)) then ! perform fixed point iterations - allocate(delta, mold=U) - delta = previous(self%steps) - do s=0, self%steps - 1 - buffer = previous(s+1) - call buffer%t_fast(t=t(s+1)) - call buffer%multiply_fast(lhs=buffer, rhs=Dt * self%b(s)) - call delta%add_fast(lhs=delta, rhs=buffer) - enddo - do s=1, iterations - buffer = U - call buffer%t_fast(t=t(self%steps) + Dt) - call buffer%multiply_fast(lhs=buffer, rhs=Dt * self%b(self%steps)) - call U%add_fast(lhs=delta, rhs=buffer) - enddo - else - buffer = U - call buffer%t_fast(t=t(self%steps) + Dt) - call buffer%multiply_fast(lhs=buffer, rhs=Dt * self%b(self%steps)) - call U%add_fast(lhs=previous(self%steps), rhs=buffer) - do s=0, self%steps - 1 - buffer = previous(s+1) - call buffer%t_fast(t=t(s+1)) - call buffer%multiply_fast(lhs=buffer, rhs=Dt * self%b(s)) - call U%add_fast(lhs=U, rhs=buffer) - enddo - endif - if (autoupdate_) call self%update_previous(U=U, previous=previous) - else - buffer = U - call buffer%t_fast(t=t(1)) - call buffer%multiply_fast(lhs=buffer, rhs=Dt * self%b(0)) - call U%add_fast(lhs=U, rhs=buffer) - endif - endsubroutine integrate_fast - - subroutine update_previous(self, U, previous) - !< Cyclic update previous time steps. - class(integrator_adams_moulton), intent(in) :: self !< Integrator. - class(integrand_object), intent(in) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand field. - integer(I_P) :: s !< Steps counter. - - if (self%steps>0) then - do s=0, self%steps - 2 - previous(s + 1) = previous(s + 2) - enddo - previous(self%steps) = U - endif - endsubroutine update_previous endmodule foodie_integrator_adams_moulton diff --git a/src/lib/foodie_integrator_backward_differentiation_formula.f90 b/src/lib/foodie_integrator_backward_differentiation_formula.f90 index 07d37d8b..52f7eb13 100644 --- a/src/lib/foodie_integrator_backward_differentiation_formula.f90 +++ b/src/lib/foodie_integrator_backward_differentiation_formula.f90 @@ -35,6 +35,7 @@ module foodie_integrator_backward_differentiation_formula use foodie_error_codes, only : ERROR_UNSUPPORTED_SCHEME use foodie_integrand_object, only : integrand_object +use foodie_integrator_multistep_object, only : integrator_multistep_object use foodie_integrator_object, only : integrator_object use penf, only : I_P, R_P @@ -51,36 +52,30 @@ module foodie_integrator_backward_differentiation_formula trim(class_name_)//'_6'] !< List of supported schemes. logical, parameter :: has_fast_mode_=.true. !< Flag to check if integrator provides *fast mode* integrate. -logical, parameter :: is_multistage_=.false. !< Flag to check if integrator is multistage. -logical, parameter :: is_multistep_=.true. !< Flag to check if integrator is multistep. -type, extends(integrator_object) :: integrator_back_df +type, extends(integrator_multistep_object) :: integrator_back_df !< FOODIE integrator: provide an implicit class of Backward-Differentiation-Formula multi-step schemes, from 1st to 6th order !< accurate. !< - !< @note The integrator must be created or initialized (initialize the *alpha* and *beta* coefficients) before used. + !< @note The integrator must be created or initialized (initialize the *a* and *b* coefficients) before used. + !< + !< @note The time steps `Dt(1:steps)` passed to the integrate methods must be identical: this integrator supports only + !< fixed time steps. private - integer(I_P) :: steps=0 !< Number of time steps. real(R_P), allocatable :: a(:) !< \(\alpha\) coefficients. real(R_P) :: b=0.0_R_P !< \(\beta\) coefficient. contains ! deferred methods procedure, pass(self) :: class_name !< Return the class name of schemes. - procedure, pass(self) :: description !< Return pretty-printed object description. procedure, pass(self) :: has_fast_mode !< Return .true. if the integrator class has *fast mode* integrate. procedure, pass(lhs) :: integr_assign_integr !< Operator `=`. - procedure, pass(self) :: is_multistage !< Return .true. for multistage integrator. - procedure, pass(self) :: is_multistep !< Return .true. for multistep integrator. + procedure, pass(self) :: integrate !< Integrate integrand field. + procedure, pass(self) :: integrate_fast !< Integrate integrand field, fast mode. procedure, pass(self) :: is_supported !< Return .true. if the integrator class support the given scheme. - procedure, pass(self) :: stages_number !< Return number of stages used. - procedure, pass(self) :: steps_number !< Return number of steps used. procedure, pass(self) :: supported_schemes !< Return the list of supported schemes. ! public methods - procedure, pass(self) :: destroy !< Destroy the integrator. - procedure, pass(self) :: initialize !< Initialize (create) the integrator. - procedure, pass(self) :: integrate !< Integrate integrand field. - procedure, pass(self) :: integrate_fast !< Integrate integrand field, fast mode. - procedure, pass(self) :: update_previous !< Cyclic update previous time steps. + procedure, pass(self) :: destroy !< Destroy the integrator. + procedure, pass(self) :: initialize !< Initialize (create) the integrator. endtype integrator_back_df contains @@ -93,25 +88,6 @@ pure function class_name(self) class_name = trim(adjustl(class_name_)) endfunction class_name - pure function description(self, prefix) result(desc) - !< Return a pretty-formatted object description. - class(integrator_back_df), intent(in) :: self !< Integrator. - character(*), intent(in), optional :: prefix !< Prefixing string. - character(len=:), allocatable :: desc !< Description. - character(len=:), allocatable :: prefix_ !< Prefixing string, local variable. - character(len=1), parameter :: NL=new_line('a') !< New line character. - integer(I_P) :: s !< Counter. - - prefix_ = '' ; if (present(prefix)) prefix_ = prefix - desc = '' - desc = desc//prefix_//'Backward-Differentiation-Formula multi-step schemes class'//NL - desc = desc//prefix_//' Supported schemes:'//NL - do s=lbound(supported_schemes_, dim=1), ubound(supported_schemes_, dim=1) - 1 - desc = desc//prefix_//' + '//supported_schemes_(s)//NL - enddo - desc = desc//prefix_//' + '//supported_schemes_(ubound(supported_schemes_, dim=1)) - endfunction description - elemental function has_fast_mode(self) !< Return .true. if the integrator class has *fast mode* integrate. class(integrator_back_df), intent(in) :: self !< Integrator. @@ -125,7 +101,7 @@ pure subroutine integr_assign_integr(lhs, rhs) class(integrator_back_df), intent(inout) :: lhs !< Left hand side. class(integrator_object), intent(in) :: rhs !< Right hand side. - call lhs%assign_abstract(rhs=rhs) + call lhs%assign_multistep(rhs=rhs) select type(rhs) class is (integrator_back_df) lhs%steps = rhs%steps @@ -134,21 +110,49 @@ pure subroutine integr_assign_integr(lhs, rhs) endselect endsubroutine integr_assign_integr - elemental function is_multistage(self) - !< Return .true. for multistage integrator. - class(integrator_back_df), intent(in) :: self !< Integrator. - logical :: is_multistage !< Inquire result. + subroutine integrate(self, U, Dt, t) + !< Integrate field with BDF class scheme. + class(integrator_back_df), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + class(integrand_object), allocatable :: delta !< Delta RHS for fixed point iterations. + integer(I_P) :: s !< Steps counter. - is_multistage = is_multistage_ - endfunction is_multistage + allocate(delta, mold=U) + delta = self%previous(self%steps) * (-self%a(self%steps)) + do s=1, self%steps - 1 + delta = delta + (self%previous(s) * (-self%a(s))) + enddo + do s=1, self%iterations + U = delta + (U%t(t=self%t(self%steps) + Dt) * (Dt * self%b)) + enddo + if (self%autoupdate) call self%update_previous(U=U, previous=self%previous, Dt=Dt, t=t, previous_t=self%t) + endsubroutine integrate - elemental function is_multistep(self) - !< Return .true. for multistage integrator. - class(integrator_back_df), intent(in) :: self !< Integrator. - logical :: is_multistep !< Inquire result. + subroutine integrate_fast(self, U, Dt, t) + !< Integrate field with BDF class scheme. + class(integrator_back_df), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + class(integrand_object), allocatable :: delta !< Delta RHS for fixed point iterations. + integer(I_P) :: s !< Steps counter. - is_multistep = is_multistep_ - endfunction is_multistep + allocate(delta, mold=U) + call delta%multiply_fast(lhs=self%previous(self%steps), rhs=-self%a(self%steps)) + do s=1, self%steps - 1 + call self%buffer%multiply_fast(lhs=self%previous(s), rhs=-self%a(s)) + call delta%add_fast(lhs=delta, rhs=self%buffer) + enddo + do s=1, self%iterations + self%buffer = U + call self%buffer%t_fast(t=self%t(self%steps) + Dt) + call self%buffer%multiply_fast(lhs=self%buffer, rhs=Dt * self%b) + call U%add_fast(lhs=delta, rhs=self%buffer) + enddo + if (self%autoupdate) call self%update_previous(U=U, previous=self%previous, Dt=Dt, t=t, previous_t=self%t) + endsubroutine integrate_fast elemental function is_supported(self, scheme) !< Return .true. if the integrator class support the given scheme. @@ -166,22 +170,6 @@ elemental function is_supported(self, scheme) enddo endfunction is_supported - elemental function stages_number(self) - !< Return number of stages used. - class(integrator_back_df), intent(in) :: self !< Integrator. - integer(I_P) :: stages_number !< Number of stages used. - - stages_number = 0 - endfunction stages_number - - elemental function steps_number(self) - !< Return number of steps used. - class(integrator_back_df), intent(in) :: self !< Integrator. - integer(I_P) :: steps_number !< Number of steps used. - - steps_number = self%steps - endfunction steps_number - pure function supported_schemes(self) result(schemes) !< Return the list of supported schemes. class(integrator_back_df), intent(in) :: self !< Integrator. @@ -196,19 +184,23 @@ elemental subroutine destroy(self) !< Destroy the integrator. class(integrator_back_df), intent(inout) :: self !< Integrator. - call self%destroy_abstract - self%steps = 0 + call self%destroy_multistep if (allocated(self%a)) deallocate(self%a) self%b = 0.0_R_P endsubroutine destroy - subroutine initialize(self, scheme) + subroutine initialize(self, scheme, iterations, autoupdate, U, stop_on_fail) !< Create the actual BDF integrator: initialize the *alpha* and *beta* coefficients. - class(integrator_back_df), intent(inout) :: self !< Integrator. - character(*), intent(in) :: scheme !< Selected scheme. + class(integrator_back_df), intent(inout) :: self !< Integrator. + character(*), intent(in) :: scheme !< Selected scheme. + integer(I_P), intent(in), optional :: iterations !< Implicit iterations. + logical, intent(in), optional :: autoupdate !< Enable cyclic autoupdate of previous time steps. + class(integrand_object), intent(in), optional :: U !< Integrand molding prototype. + logical, intent(in), optional :: stop_on_fail !< Stop execution if initialization fail. if (self%is_supported(scheme=scheme)) then - call self%destroy + call self%destroy + self%description_ = trim(adjustl(scheme)) select case(trim(adjustl(scheme))) case('back_df_1') self%steps = 1 ; allocate(self%a(1:self%steps)) ; self%a = 0.0_R_P @@ -251,82 +243,14 @@ subroutine initialize(self, scheme) self%b = 60.0_R_P/147.0_R_P case default endselect + self%autoupdate = .true. ; if (present(autoupdate)) self%autoupdate = autoupdate + self%iterations = 1 ; if (present(iterations)) self%iterations = iterations + self%registers = self%steps + if (present(U)) call self%allocate_integrand_members(U=U) else call self%trigger_error(error=ERROR_UNSUPPORTED_SCHEME, & error_message='"'//trim(adjustl(scheme))//'" unsupported scheme', & - is_severe=.true.) + is_severe=stop_on_fail) endif endsubroutine initialize - - subroutine integrate(self, U, previous, Dt, t, iterations, autoupdate) - !< Integrate field with BDF class scheme. - class(integrator_back_df), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand field. - real(R_P), intent(in) :: Dt !< Time steps. - real(R_P), intent(in) :: t(:) !< Times. - integer(I_P), optional, intent(in) :: iterations !< Fixed point iterations. - logical, optional, intent(in) :: autoupdate !< Perform cyclic autoupdate of previous time steps. - integer(I_P) :: iterations_ !< Fixed point iterations. - logical :: autoupdate_ !< Perform cyclic autoupdate of previous time steps, dummy var. - class(integrand_object), allocatable :: delta !< Delta RHS for fixed point iterations. - integer(I_P) :: s !< Steps counter. - - autoupdate_ = .true. ; if (present(autoupdate)) autoupdate_ = autoupdate - iterations_ = 1 ; if (present(iterations)) iterations_ = iterations - allocate(delta, mold=U) - delta = previous(self%steps) * (-self%a(self%steps)) - do s=1, self%steps - 1 - delta = delta + (previous(s) * (-self%a(s))) - enddo - do s=1, iterations_ - U = delta + (U%t(t=t(self%steps) + Dt) * (Dt * self%b)) - enddo - if (autoupdate_) call self%update_previous(U=U, previous=previous) - endsubroutine integrate - - subroutine integrate_fast(self, U, previous, buffer, Dt, t, iterations, autoupdate) - !< Integrate field with BDF class scheme. - class(integrator_back_df), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand field. - class(integrand_object), intent(inout) :: buffer !< Temporary buffer for doing fast operation. - real(R_P), intent(in) :: Dt !< Time steps. - real(R_P), intent(in) :: t(:) !< Times. - integer(I_P), optional, intent(in) :: iterations !< Fixed point iterations. - logical, optional, intent(in) :: autoupdate !< Perform cyclic autoupdate of previous time steps. - integer(I_P) :: iterations_ !< Fixed point iterations. - logical :: autoupdate_ !< Perform cyclic autoupdate of previous time steps, dummy var. - class(integrand_object), allocatable :: delta !< Delta RHS for fixed point iterations. - integer(I_P) :: s !< Steps counter. - - autoupdate_ = .true. ; if (present(autoupdate)) autoupdate_ = autoupdate - iterations_ = 1 ; if (present(iterations)) iterations_ = iterations - allocate(delta, mold=U) - call delta%multiply_fast(lhs=previous(self%steps), rhs=-self%a(self%steps)) - do s=1, self%steps - 1 - call buffer%multiply_fast(lhs=previous(s), rhs=-self%a(s)) - call delta%add_fast(lhs=delta, rhs=buffer) - enddo - do s=1, iterations - buffer = U - call buffer%t_fast(t=t(self%steps) + Dt) - call buffer%multiply_fast(lhs=buffer, rhs=Dt * self%b) - call U%add_fast(lhs=delta, rhs=buffer) - enddo - if (autoupdate_) call self%update_previous(U=U, previous=previous) - endsubroutine integrate_fast - - subroutine update_previous(self, U, previous) - !< Cyclic update previous time steps. - class(integrator_back_df), intent(in) :: self !< Integrator. - class(integrand_object), intent(in) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand field. - integer(I_P) :: s !< Steps counter. - - do s=1, self%steps - 1 - previous(s) = previous(s + 1) - enddo - previous(self%steps) = U - endsubroutine update_previous endmodule foodie_integrator_backward_differentiation_formula diff --git a/src/lib/foodie_integrator_euler_explicit.f90 b/src/lib/foodie_integrator_euler_explicit.f90 index 0d8856e3..90b1f030 100644 --- a/src/lib/foodie_integrator_euler_explicit.f90 +++ b/src/lib/foodie_integrator_euler_explicit.f90 @@ -14,7 +14,9 @@ module foodie_integrator_euler_explicit !< !< @note The value of \(\Delta t\) must be provided, it not being computed by the integrator. +use foodie_error_codes, only : ERROR_UNSUPPORTED_SCHEME use foodie_integrand_object, only : integrand_object +use foodie_integrator_multistage_object, only : integrator_multistage_object use foodie_integrator_object, only : integrator_object use penf, only : I_P, R_P @@ -25,30 +27,22 @@ module foodie_integrator_euler_explicit character(len=99), parameter :: class_name_='euler_explicit' !< Name of the class of schemes. character(len=99), parameter :: supported_schemes_(1:1)=[trim(class_name_)] !< List of supported schemes. -logical, parameter :: has_fast_mode_=.true. !< Flag to check if integrator provides *fast mode* integrate. -logical, parameter :: is_multistage_=.false. !< Flag to check if integrator is multistage. -logical, parameter :: is_multistep_=.false. !< Flag to check if integrator is multistep. +logical, parameter :: has_fast_mode_=.true. !< Flag to check if integrator provides *fast mode* integrate. -type, extends(integrator_object) :: integrator_euler_explicit +type, extends(integrator_multistage_object) :: integrator_euler_explicit !< FOODIE integrator: provide explicit Euler scheme, it being 1st order accurate. - !< - !< @note The integrator can be used directly without any initialization. contains ! deferred methods procedure, pass(self) :: class_name !< Return the class name of schemes. - procedure, pass(self) :: description !< Return pretty-printed object description. procedure, pass(self) :: has_fast_mode !< Return .true. if the integrator class has *fast mode* integrate. procedure, pass(lhs) :: integr_assign_integr !< Operator `=`. - procedure, pass(self) :: is_multistage !< Return .true. for multistage integrator. - procedure, pass(self) :: is_multistep !< Return .true. for multistep integrator. + procedure, pass(self) :: integrate !< Integrate integrand field. + procedure, pass(self) :: integrate_fast !< Integrate integrand field, fast mode. procedure, pass(self) :: is_supported !< Return .true. if the integrator class support the given scheme. - procedure, pass(self) :: stages_number !< Return number of stages used. - procedure, pass(self) :: steps_number !< Return number of steps used. procedure, pass(self) :: supported_schemes !< Return the list of supported schemes. ! public methods - procedure, pass(self) :: destroy !< Destroy the integrator. - procedure, nopass :: integrate !< Integrate integrand field. - procedure, nopass :: integrate_fast !< Integrate integrand field, fast mode. + procedure, pass(self) :: destroy !< Destroy the integrator. + procedure, pass(self) :: initialize !< Initialize (create) the integrator. endtype integrator_euler_explicit contains ! deferred methods @@ -60,17 +54,6 @@ pure function class_name(self) class_name = trim(adjustl(class_name_)) endfunction class_name - pure function description(self, prefix) result(desc) - !< Return a pretty-formatted object description. - class(integrator_euler_explicit), intent(in) :: self !< Integrator. - character(*), intent(in), optional :: prefix !< Prefixing string. - character(len=:), allocatable :: desc !< Description. - character(len=:), allocatable :: prefix_ !< Prefixing string, local variable. - - prefix_ = '' ; if (present(prefix)) prefix_ = prefix - desc = prefix_//'Euler, Explicit (1 step/stage) 1st order scheme' - endfunction description - elemental function has_fast_mode(self) !< Return .true. if the integrator class has *fast mode* integrate. class(integrator_euler_explicit), intent(in) :: self !< Integrator. @@ -84,24 +67,35 @@ pure subroutine integr_assign_integr(lhs, rhs) class(integrator_euler_explicit), intent(inout) :: lhs !< Left hand side. class(integrator_object), intent(in) :: rhs !< Right hand side. - call lhs%assign_abstract(rhs=rhs) + call lhs%assign_multistage(rhs=rhs) endsubroutine integr_assign_integr - elemental function is_multistage(self) - !< Return .true. for multistage integrator. - class(integrator_euler_explicit), intent(in) :: self !< Integrator. - logical :: is_multistage !< Inquire result. - - is_multistage = is_multistage_ - endfunction is_multistage + subroutine integrate(self, U, Dt, t, new_Dt) + !< Integrate field with explicit Euler scheme, 1st order. + class(integrator_euler_explicit), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + real(R_P), optional, intent(out) :: new_Dt !< New adapted time step. - elemental function is_multistep(self) - !< Return .true. for multistage integrator. - class(integrator_euler_explicit), intent(in) :: self !< Integrator. - logical :: is_multistep !< Inquire result. + U = U + (U%t(t=t) * Dt) + if (present(new_Dt)) new_Dt = Dt + endsubroutine integrate - is_multistep = is_multistep_ - endfunction is_multistep + subroutine integrate_fast(self, U, Dt, t, new_Dt) + !< Integrate field with explicit Euler scheme, 1st order, fast mode. + class(integrator_euler_explicit), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + real(R_P), optional, intent(out) :: new_Dt !< New adapted time step. + + self%buffer = U + call self%buffer%t_fast(t=t) + call self%buffer%multiply_fast(lhs=self%buffer, rhs=Dt) + call U%add_fast(lhs=U, rhs=self%buffer) + if (present(new_Dt)) new_Dt = Dt + endsubroutine integrate_fast elemental function is_supported(self, scheme) !< Return .true. if the integrator class support the given scheme. @@ -119,22 +113,6 @@ elemental function is_supported(self, scheme) enddo endfunction is_supported - elemental function stages_number(self) - !< Return number of stages used. - class(integrator_euler_explicit), intent(in) :: self !< Integrator. - integer(I_P) :: stages_number !< Number of stages used. - - stages_number = 1 - endfunction stages_number - - elemental function steps_number(self) - !< Return number of steps used. - class(integrator_euler_explicit), intent(in) :: self !< Integrator. - integer(I_P) :: steps_number !< Number of steps used. - - steps_number = 1 - endfunction steps_number - pure function supported_schemes(self) result(schemes) !< Return the list of supported schemes. class(integrator_euler_explicit), intent(in) :: self !< Integrator. @@ -149,28 +127,26 @@ elemental subroutine destroy(self) !< Destroy the integrator. class(integrator_euler_explicit), intent(inout) :: self !< Integrator. - call self%destroy_abstract + call self%destroy_multistage endsubroutine destroy - subroutine integrate(U, Dt, t) - !< Integrate field with explicit Euler scheme, 1st order. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - real(R_P), intent(in) :: Dt !< Time step. - real(R_P), optional, intent(in) :: t !< Time. - - U = U + (U%t(t=t) * Dt) - endsubroutine integrate - - subroutine integrate_fast(U, buffer, Dt, t) - !< Integrate field with explicit Euler scheme, 1st order, fast mode. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: buffer !< Temporary buffer for doing fast operation. - real(R_P), intent(in) :: Dt !< Time step. - real(R_P), optional, intent(in) :: t !< Time. - - buffer = U - call buffer%t_fast(t=t) - call buffer%multiply_fast(lhs=buffer, rhs=Dt) - call U%add_fast(lhs=U, rhs=buffer) - endsubroutine integrate_fast + subroutine initialize(self, scheme, U, stop_on_fail) + !< Create the actual RK integrator: initialize the Butcher' table coefficients. + class(integrator_euler_explicit), intent(inout) :: self !< Integrator. + character(*), intent(in) :: scheme !< Selected scheme. + class(integrand_object), intent(in), optional :: U !< Integrand molding prototype. + logical, intent(in), optional :: stop_on_fail !< Stop execution if initialization fail. + + if (self%is_supported(scheme=scheme)) then + call self%destroy + self%description_ = trim(adjustl(scheme)) + self%stages = 0 + self%registers = self%stages + if (present(U)) call self%allocate_integrand_members(U=U) + else + call self%trigger_error(error=ERROR_UNSUPPORTED_SCHEME, & + error_message='"'//trim(adjustl(scheme))//'" unsupported scheme', & + is_severe=stop_on_fail) + endif + endsubroutine initialize endmodule foodie_integrator_euler_explicit diff --git a/src/lib/foodie_integrator_leapfrog.f90 b/src/lib/foodie_integrator_leapfrog.f90 index b3b72a1e..697af33c 100644 --- a/src/lib/foodie_integrator_leapfrog.f90 +++ b/src/lib/foodie_integrator_leapfrog.f90 @@ -38,6 +38,7 @@ module foodie_integrator_leapfrog use foodie_error_codes, only : ERROR_UNSUPPORTED_SCHEME use foodie_integrand_object, only : integrand_object +use foodie_integrator_multistep_object, only : integrator_multistep_object use foodie_integrator_object, only : integrator_object use penf, only : I_P, R_P @@ -50,35 +51,33 @@ module foodie_integrator_leapfrog trim(class_name_)//'_raw'] !< List of supported schemes. logical, parameter :: has_fast_mode_=.true. !< Flag to check if integrator provides *fast mode* integrate. -logical, parameter :: is_multistage_=.false. !< Flag to check if integrator is multistage. -logical, parameter :: is_multistep_=.true. !< Flag to check if integrator is multistep. -type, extends(integrator_object) :: integrator_leapfrog +type, extends(integrator_multistep_object) :: integrator_leapfrog !< FOODIE integrator: provide an explicit class of leapfrog multi-step schemes, 2nd order accurate. !< - !< @note The integrator could be used without initialialization (initialize the time filter coefficients) if the defulat values - !< are suitable for the problem. + !< @note The integrator must be initialized before used. + !< + !< @note The time steps `Dt(1:steps)` passed to the integrate methods must be identical: this integrator supports only + !< fixed time steps. private - integer(I_P) :: steps=2 !< Number of time steps. - real(R_P) :: nu=0.01_R_P !< Robert-Asselin filter coefficient. - real(R_P) :: alpha=0.53_R_P !< Robert-Asselin-Williams filter coefficient. + real(R_P) :: nu !< Robert-Asselin filter coefficient. + real(R_P) :: alpha !< Robert-Asselin-Williams filter coefficient. + logical :: is_filtered !< Flag to check if the integration if RAW filtered. + class(integrand_object), allocatable :: filter !< Filter field displacement. contains ! deferred methods procedure, pass(self) :: class_name !< Return the class name of schemes. - procedure, pass(self) :: description !< Return pretty-printed object description. procedure, pass(self) :: has_fast_mode !< Return .true. if the integrator class has *fast mode* integrate. procedure, pass(lhs) :: integr_assign_integr !< Operator `=`. - procedure, pass(self) :: is_multistage !< Return .true. for multistage integrator. - procedure, pass(self) :: is_multistep !< Return .true. for multistep integrator. + procedure, pass(self) :: integrate !< Integrate integrand field. + procedure, pass(self) :: integrate_fast !< Integrate integrand field, fast mode. procedure, pass(self) :: is_supported !< Return .true. if the integrator class support the given scheme. - procedure, pass(self) :: stages_number !< Return number of stages used. - procedure, pass(self) :: steps_number !< Return number of steps used. procedure, pass(self) :: supported_schemes !< Return the list of supported schemes. ! public methods - procedure, pass(self) :: destroy !< Destroy the integrator. - procedure, pass(self) :: initialize !< Initialize (create) the integrator. - procedure, pass(self) :: integrate !< Integrate integrand field. - procedure, pass(self) :: integrate_fast !< Integrate integrand field, fast mode. + procedure, pass(self) :: destroy !< Destroy the integrator. + procedure, pass(self) :: initialize !< Initialize (create) the integrator. + ! overridden public methods + procedure, pass(self) :: allocate_integrand_members !< Allocate integrand members. endtype integrator_leapfrog contains @@ -91,17 +90,6 @@ pure function class_name(self) class_name = trim(adjustl(class_name_)) endfunction class_name - pure function description(self, prefix) result(desc) - !< Return a pretty-formatted object description. - class(integrator_leapfrog), intent(in) :: self !< Integrator. - character(*), intent(in), optional :: prefix !< Prefixing string. - character(len=:), allocatable :: desc !< Description. - character(len=:), allocatable :: prefix_ !< Prefixing string, local variable. - - prefix_ = '' ; if (present(prefix)) prefix_ = prefix - desc = desc//prefix_//'Explicit leapfrog multi-step 2nd order scheme' - endfunction description - elemental function has_fast_mode(self) !< Return .true. if the integrator class has *fast mode* integrate. class(integrator_leapfrog), intent(in) :: self !< Integrator. @@ -115,30 +103,61 @@ pure subroutine integr_assign_integr(lhs, rhs) class(integrator_leapfrog), intent(inout) :: lhs !< Left hand side. class(integrator_object), intent(in) :: rhs !< Right hand side. - call lhs%assign_abstract(rhs=rhs) + call lhs%assign_multistep(rhs=rhs) select type(rhs) class is(integrator_leapfrog) - lhs%steps = rhs%steps - lhs%nu = rhs%nu - lhs%alpha = rhs%alpha + lhs%nu = rhs%nu + lhs%alpha = rhs%alpha + lhs%is_filtered = rhs%is_filtered + if (allocated(lhs%filter)) deallocate(lhs%filter) + if (allocated(rhs%filter)) then + allocate(lhs%filter, mold=rhs%filter) + lhs%filter = rhs%filter + endif endselect endsubroutine integr_assign_integr - elemental function is_multistage(self) - !< Return .true. for multistage integrator. - class(integrator_leapfrog), intent(in) :: self !< Integrator. - logical :: is_multistage !< Inquire result. - - is_multistage = is_multistage_ - endfunction is_multistage - - elemental function is_multistep(self) - !< Return .true. for multistage integrator. - class(integrator_leapfrog), intent(in) :: self !< Integrator. - logical :: is_multistep !< Inquire result. + subroutine integrate(self, U, Dt, t) + !< Integrate field with leapfrog class scheme. + class(integrator_leapfrog), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + + U = self%previous(1) + (self%previous(2)%t(t=t) * (Dt * 2._R_P)) + if (self%is_filtered) then + self%filter = (self%previous(1) - (self%previous(2) * 2._R_P) + U) * self%nu * 0.5_R_P + self%previous(2) = self%previous(2) + (self%filter * self%alpha) + U = U + (self%filter * (self%alpha - 1._R_P)) + endif + if (self%autoupdate) call self%update_previous(U=U, previous=self%previous) + endsubroutine integrate - is_multistep = is_multistep_ - endfunction is_multistep + subroutine integrate_fast(self, U, Dt, t) + !< Integrate field with leapfrog class scheme, fast mode. + class(integrator_leapfrog), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + + self%buffer = self%previous(2) + call self%buffer%t_fast(t=t) + call self%buffer%multiply_fast(lhs=self%buffer, rhs=Dt * 2._R_P) + call U%add_fast(lhs=self%previous(1), rhs=self%buffer) + if (self%is_filtered) then + call self%buffer%multiply_fast(lhs=self%previous(2), rhs=2._R_P) + call self%buffer%subtract_fast(lhs=self%previous(1), rhs=self%buffer) + call self%buffer%add_fast(lhs=self%buffer, rhs=U) + call self%filter%multiply_fast(lhs=self%buffer, rhs=self%nu * 0.5_R_P) + + call self%buffer%multiply_fast(lhs=self%filter, rhs=self%alpha) + call self%previous(2)%add_fast(lhs=self%previous(2), rhs=self%buffer) + + call self%buffer%multiply_fast(lhs=self%filter, rhs=self%alpha - 1._R_P) + call U%add_fast(lhs=U, rhs=self%buffer) + endif + if (self%autoupdate) call self%update_previous(U=U, previous=self%previous) + endsubroutine integrate_fast elemental function is_supported(self, scheme) !< Return .true. if the integrator class support the given scheme. @@ -156,22 +175,6 @@ elemental function is_supported(self, scheme) enddo endfunction is_supported - elemental function stages_number(self) - !< Return number of stages used. - class(integrator_leapfrog), intent(in) :: self !< Integrator. - integer(I_P) :: stages_number !< Number of stages used. - - stages_number = 0 - endfunction stages_number - - elemental function steps_number(self) - !< Return number of steps used. - class(integrator_leapfrog), intent(in) :: self !< Integrator. - integer(I_P) :: steps_number !< Number of steps used. - - steps_number = self%steps - endfunction steps_number - pure function supported_schemes(self) result(schemes) !< Return the list of supported schemes. class(integrator_leapfrog), intent(in) :: self !< Integrator. @@ -186,76 +189,72 @@ elemental subroutine destroy(self) !< Destroy the integrator. class(integrator_leapfrog), intent(INOUT) :: self !< Integrator. - call self%destroy_abstract - self%steps = 2 - self%nu = 0.01_R_P - self%alpha = 0.53_R_P + call self%destroy_multistep + self%nu = 0._R_P + self%alpha = 0._R_P + self%is_filtered = .false. + if (allocated(self%filter)) deallocate(self%filter) endsubroutine destroy - subroutine initialize(self, scheme, nu, alpha) + subroutine initialize(self, scheme, nu, alpha, autoupdate, U, stop_on_fail) !< Create the actual leapfrog integrator: initialize the filter coefficient. - class(integrator_leapfrog), intent(inout) :: self !< Integrator. - character(*), intent(in) :: scheme !< Selected scheme. - real(R_P), intent(in), optional :: nu !< Williams-Robert-Asselin filter coefficient. - real(R_P), intent(in), optional :: alpha !< Robert-Asselin filter coefficient. + class(integrator_leapfrog), intent(inout) :: self !< Integrator. + character(*), intent(in) :: scheme !< Selected scheme. + real(R_P), intent(in), optional :: nu !< Williams-Robert-Asselin filter coefficient. + real(R_P), intent(in), optional :: alpha !< Robert-Asselin filter coefficient. + logical, intent(in), optional :: autoupdate !< Enable cyclic autoupdate of previous time steps. + class(integrand_object), intent(in), optional :: U !< Integrand molding prototype. + logical, intent(in), optional :: stop_on_fail !< Stop execution if initialization fail. if (self%is_supported(scheme=scheme)) then - call self%destroy - if (present(nu)) self%nu = nu - if (present(alpha)) self%alpha = alpha + call self%destroy + self%description_ = trim(adjustl(scheme)) + select case(trim(adjustl(scheme))) + case('leapfrog_raw') + self%nu = 0.01_R_P ; if (present(nu)) self%nu = nu + self%alpha = 0.53_R_P ; if (present(alpha)) self%alpha = alpha + self%is_filtered = .true. + endselect + self%autoupdate = .true. ; if (present(autoupdate)) self%autoupdate = autoupdate + self%steps = 2 + self%registers = self%steps + if (present(U)) call self%allocate_integrand_members(U=U) else - call self%trigger_error(error=ERROR_UNSUPPORTED_SCHEME, & - error_message='"'//trim(adjustl(scheme))//'" unsupported scheme', & - is_severe=.true.) + call self%trigger_error(error=ERROR_UNSUPPORTED_SCHEME, & + error_message='"'//trim(adjustl(scheme))//'" unsupported scheme', & + is_severe=stop_on_fail) endif endsubroutine initialize - subroutine integrate(self, U, previous, Dt, t, filter) - !< Integrate field with leapfrog class scheme. - class(integrator_leapfrog), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:2) !< Previous time steps solutions of integrand field. - real(R_P), intent(in) :: Dt !< Time step. - real(R_P), intent(in) :: t !< Time. - class(integrand_object), optional, intent(inout) :: filter !< Filter field displacement. - - U = previous(1) + (previous(2)%t(t=t) * (Dt * 2._R_P)) - if (present(filter)) then - filter = (previous(1) - (previous(2) * 2._R_P) + U) * self%nu * 0.5_R_P - previous(2) = previous(2) + (filter * self%alpha) - U = U + (filter * (self%alpha - 1._R_P)) - endif - previous(1) = previous(2) - previous(2) = U - endsubroutine integrate - - subroutine integrate_fast(self, U, previous, buffer, Dt, t, filter) - !< Integrate field with leapfrog class scheme, fast mode. - class(integrator_leapfrog), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:2) !< Previous time steps solutions of integrand field. - class(integrand_object), intent(inout) :: buffer !< Temporary buffer for doing fast operation. - real(R_P), intent(in) :: Dt !< Time step. - real(R_P), intent(in) :: t !< Time. - class(integrand_object), optional, intent(inout) :: filter !< Filter field displacement. - - buffer = previous(2) - call buffer%t_fast(t=t) - call buffer%multiply_fast(lhs=buffer, rhs=Dt * 2._R_P) - call U%add_fast(lhs=previous(1), rhs=buffer) - if (present(filter)) then - call buffer%multiply_fast(lhs=previous(2), rhs=2._R_P) - call buffer%subtract_fast(lhs=previous(1), rhs=buffer) - call buffer%add_fast(lhs=buffer, rhs=U) - call filter%multiply_fast(lhs=buffer, rhs=self%nu * 0.5_R_P) - - call buffer%multiply_fast(lhs=filter, rhs=self%alpha) - call previous(2)%add_fast(lhs=previous(2), rhs=buffer) - - call buffer%multiply_fast(lhs=filter, rhs=self%alpha - 1._R_P) - call U%add_fast(lhs=U, rhs=buffer) - endif - previous(1) = previous(2) - previous(2) = U - endsubroutine integrate_fast + ! overridden public methods + pure subroutine allocate_integrand_members(self, U) + !< Allocate members of interpolator being of [[integrand_object]] class. + !< + !< @note It is assumed that the integrator has been properly initialized before calling this method. + class(integrator_leapfrog), intent(inout) :: self !< Integrator. + class(integrand_object), intent(in) :: U !< Integrand. + integer(I_P) :: s !< Counter. + + if (self%is_multistep() .and. self%registers > 0) then + if (allocated(self%Dt)) deallocate(self%Dt) + allocate(self%Dt(1:self%registers)) ; self%Dt = 0._R_P + if (allocated(self%t)) deallocate(self%t) + allocate(self%t(1:self%registers)) ; self%t = 0._R_P + if (allocated(self%previous)) deallocate(self%previous) + allocate(self%previous(1:self%registers), mold=U) + do s=1, self%registers + self%previous(s) = U + enddo + endif + if (self%has_fast_mode()) then + if (allocated(self%buffer)) deallocate(self%buffer) + allocate(self%buffer, mold=U) + self%buffer = U + endif + if (self%is_filtered) then + if (allocated(self%filter)) deallocate(self%filter) + allocate(self%filter, mold=U) + self%filter = U + endif + endsubroutine allocate_integrand_members endmodule foodie_integrator_leapfrog diff --git a/src/lib/foodie_integrator_lmm_ssp.f90 b/src/lib/foodie_integrator_lmm_ssp.f90 index 07f90dd7..6916cd9d 100644 --- a/src/lib/foodie_integrator_lmm_ssp.f90 +++ b/src/lib/foodie_integrator_lmm_ssp.f90 @@ -28,6 +28,7 @@ module foodie_integrator_lmm_ssp use foodie_error_codes, only : ERROR_UNSUPPORTED_SCHEME use foodie_integrand_object, only : integrand_object +use foodie_integrator_multistep_object, only : integrator_multistep_object use foodie_integrator_object, only : integrator_object use penf, only : I_P, R_P @@ -41,36 +42,30 @@ module foodie_integrator_lmm_ssp trim(class_name_)//'_steps_5_order_3'] !< List of supported schemes. logical, parameter :: has_fast_mode_=.true. !< Flag to check if integrator provides *fast mode* integrate. -logical, parameter :: is_multistage_=.false. !< Flag to check if integrator is multistage. -logical, parameter :: is_multistep_=.true. !< Flag to check if integrator is multistep. -type, extends(integrator_object) :: integrator_lmm_ssp +type, extends(integrator_multistep_object) :: integrator_lmm_ssp !< FOODIE integrator: provide an explicit class of Linear Multi-step Methods (LLM) with Strong Stability Preserving property, !< from 2nd to 3rd order accurate. !< - !< @note The integrator must be created or initialized (initialize the *a,b* coefficients) before used. + !< @note The integrator must be initialized before used. + !< + !< @note The time steps `Dt(1:steps)` passed to the integrate methods must be identical: this integrator supports only + !< fixed time steps. private - integer(I_P) :: steps=0 !< Number of time steps. - real(R_P), allocatable :: a(:) !< *a* coefficients. - real(R_P), allocatable :: b(:) !< *b* coefficients. + real(R_P), allocatable :: a(:) !< *a* coefficients. + real(R_P), allocatable :: b(:) !< *b* coefficients. contains ! deferred methods procedure, pass(self) :: class_name !< Return the class name of schemes. - procedure, pass(self) :: description !< Return pretty-printed object description. procedure, pass(self) :: has_fast_mode !< Return .true. if the integrator class has *fast mode* integrate. procedure, pass(lhs) :: integr_assign_integr !< Operator `=`. - procedure, pass(self) :: is_multistage !< Return .true. for multistage integrator. - procedure, pass(self) :: is_multistep !< Return .true. for multistep integrator. + procedure, pass(self) :: integrate !< Integrate integrand field. + procedure, pass(self) :: integrate_fast !< Integrate integrand field, fast mode. procedure, pass(self) :: is_supported !< Return .true. if the integrator class support the given scheme. - procedure, pass(self) :: stages_number !< Return number of stages used. - procedure, pass(self) :: steps_number !< Return number of steps used. procedure, pass(self) :: supported_schemes !< Return the list of supported schemes. ! public methods - procedure, pass(self) :: destroy !< Destroy the integrator. - procedure, pass(self) :: initialize !< Initialize (create) the integrator. - procedure, pass(self) :: integrate !< Integrate integrand field. - procedure, pass(self) :: integrate_fast !< Integrate integrand field, fast mode. - procedure, pass(self) :: update_previous !< Cyclic update previous time steps. + procedure, pass(self) :: destroy !< Destroy the integrator. + procedure, pass(self) :: initialize !< Initialize (create) the integrator. endtype integrator_lmm_ssp contains @@ -83,25 +78,6 @@ pure function class_name(self) class_name = trim(adjustl(class_name_)) endfunction class_name - pure function description(self, prefix) result(desc) - !< Return a pretty-formatted object description. - class(integrator_lmm_ssp), intent(in) :: self !< Integrator. - character(*), intent(in), optional :: prefix !< Prefixing string. - character(len=:), allocatable :: desc !< Description. - character(len=:), allocatable :: prefix_ !< Prefixing string, local variable. - character(len=1), parameter :: NL=new_line('a') !< New line character. - integer(I_P) :: s !< Counter. - - prefix_ = '' ; if (present(prefix)) prefix_ = prefix - desc = '' - desc = desc//prefix_//'Strong Stability preserving Linear-Multistep-Methods class'//NL - desc = desc//prefix_//' Supported schemes:'//NL - do s=lbound(supported_schemes_, dim=1), ubound(supported_schemes_, dim=1) - 1 - desc = desc//prefix_//' + '//supported_schemes_(s)//NL - enddo - desc = desc//prefix_//' + '//supported_schemes_(ubound(supported_schemes_, dim=1)) - endfunction description - elemental function has_fast_mode(self) !< Return .true. if the integrator class has *fast mode* integrate. class(integrator_lmm_ssp), intent(in) :: self !< Integrator. @@ -124,21 +100,45 @@ pure subroutine integr_assign_integr(lhs, rhs) endselect endsubroutine integr_assign_integr - elemental function is_multistage(self) - !< Return .true. for multistage integrator. - class(integrator_lmm_ssp), intent(in) :: self !< Integrator. - logical :: is_multistage !< Inquire result. + subroutine integrate(self, U, Dt, t) + !< Integrate field with LMM-SSP class scheme. + class(integrator_lmm_ssp), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + integer(I_P) :: s !< Steps counter. - is_multistage = is_multistage_ - endfunction is_multistage + U = U * 0._R_P + do s=1, self%steps + if (self%a(s) /= 0._R_P) U = U + (self%previous(s) * self%a(s)) + if (self%b(s) /= 0._R_P) U = U + (self%previous(s)%t(t=self%t(s)) * (Dt * self%b(s))) + enddo + if (self%autoupdate) call self%update_previous(U=U, previous=self%previous, Dt=Dt, t=t, previous_t=self%t) + endsubroutine integrate - elemental function is_multistep(self) - !< Return .true. for multistage integrator. - class(integrator_lmm_ssp), intent(in) :: self !< Integrator. - logical :: is_multistep !< Inquire result. + subroutine integrate_fast(self, U, Dt, t) + !< Integrate field with LMM-SSP class scheme, fast mode. + class(integrator_lmm_ssp), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + integer(I_P) :: s !< Steps counter. - is_multistep = is_multistep_ - endfunction is_multistep + call U%multiply_fast(lhs=U, rhs=0._R_P) + do s=1, self%steps + if (self%a(s) /= 0._R_P) then + call self%buffer%multiply_fast(lhs=self%previous(s), rhs=self%a(s)) + call U%add_fast(lhs=U, rhs=self%buffer) + endif + if (self%b(s) /= 0._R_P) then + self%buffer = self%previous(s) + call self%buffer%t_fast(t=self%t(s)) + call self%buffer%multiply_fast(lhs=self%buffer, rhs=Dt * self%b(s)) + call U%add_fast(lhs=U, rhs=self%buffer) + endif + enddo + if (self%autoupdate) call self%update_previous(U=U, previous=self%previous, Dt=Dt, t=t, previous_t=self%t) + endsubroutine integrate_fast elemental function is_supported(self, scheme) !< Return .true. if the integrator class support the given scheme. @@ -156,22 +156,6 @@ elemental function is_supported(self, scheme) enddo endfunction is_supported - elemental function stages_number(self) - !< Return number of stages used. - class(integrator_lmm_ssp), intent(in) :: self !< Integrator. - integer(I_P) :: stages_number !< Number of stages used. - - stages_number = 0 - endfunction stages_number - - elemental function steps_number(self) - !< Return number of steps used. - class(integrator_lmm_ssp), intent(in) :: self !< Integrator. - integer(I_P) :: steps_number !< Number of steps used. - - steps_number = self%steps - endfunction steps_number - pure function supported_schemes(self) result(schemes) !< Return the list of supported schemes. class(integrator_lmm_ssp), intent(in) :: self !< Integrator. @@ -186,129 +170,73 @@ elemental subroutine destroy(self) !< Destroy the integrator. class(integrator_lmm_ssp), intent(inout) :: self !< Integrator. - call self%destroy_abstract - self%steps = 0 + call self%destroy_multistep if (allocated(self%a)) deallocate(self%a) if (allocated(self%b)) deallocate(self%b) endsubroutine destroy - subroutine initialize(self, scheme) - !< Create the actual LMM-SSP integrator: initialize the *a,b* coefficients. - !< - !< @note If the integrator is initialized with a bad (unsupported) number of required time steps the initialization fails and - !< the integrator error status is updated consistently for external-provided errors handling. - class(integrator_lmm_ssp), intent(inout) :: self !< Integrator. - character(*), intent(in) :: scheme !< Selected scheme. - - if (self%is_supported(scheme=scheme)) then - call self%destroy - select case(trim(adjustl(scheme))) - case('lmm_ssp_steps_3_order_2') - self%steps = 3 - allocate(self%a(1:self%steps)) ; self%a = 0.0_R_P - allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P - self%a(1) = 1._R_P/4._R_P - self%a(2) = 0._R_P - self%a(3) = 3._R_P/4._R_P - - self%b(1) = 0._R_P - self%b(2) = 0._R_P - self%b(3) = 3._R_P/2._R_P - case('lmm_ssp_steps_4_order_3') - self%steps = 4 - allocate(self%a(1:self%steps)) ; self%a = 0.0_R_P - allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P - self%a(1) = 11._R_P/27._R_P - self%a(2) = 0._R_P - self%a(3) = 0._R_P - self%a(4) = 16._R_P/27._R_P - - self%b(1) = 12._R_P/27._R_P - self%b(2) = 0._R_P - self%b(3) = 0._R_P - self%b(4) = 16._R_P/9._R_P - case('lmm_ssp_steps_5_order_3') - self%steps = 5 - allocate(self%a(1:self%steps)) ; self%a = 0.0_R_P - allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P - self%a(1) = 7._R_P/32._R_P - self%a(2) = 0._R_P - self%a(3) = 0._R_P - self%a(4) = 0._R_P - self%a(5) = 25._R_P/32._R_P - - self%b(1) = 5._R_P/16._R_P - self%b(2) = 0._R_P - self%b(3) = 0._R_P - self%b(4) = 0._R_P - self%b(5) = 25._R_P/16._R_P - endselect - else - call self%trigger_error(error=ERROR_UNSUPPORTED_SCHEME, & - error_message='"'//trim(adjustl(scheme))//'" unsupported scheme', & - is_severe=.true.) - endif - endsubroutine initialize - - subroutine integrate(self, U, previous, Dt, t, autoupdate) - !< Integrate field with LMM-SSP class scheme. - class(integrator_lmm_ssp), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand field. - real(R_P), intent(in) :: Dt !< Time steps. - real(R_P), intent(in) :: t(:) !< Times. - logical, optional, intent(in) :: autoupdate !< Perform cyclic autoupdate of previous time steps. - logical :: autoupdate_ !< Perform cyclic autoupdate of previous time steps, dummy var. - integer(I_P) :: s !< Steps counter. - - autoupdate_ = .true. ; if (present(autoupdate)) autoupdate_ = autoupdate - U = U * 0._R_P - do s=1, self%steps - if (self%a(s) /= 0._R_P) U = U + (previous(s) * self%a(s)) - if (self%b(s) /= 0._R_P) U = U + (previous(s)%t(t=t(s)) * (Dt * self%b(s))) - enddo - if (autoupdate_) call self%update_previous(U=U, previous=previous) - endsubroutine integrate - - subroutine integrate_fast(self, U, previous, buffer, Dt, t, autoupdate) - !< Integrate field with LMM-SSP class scheme, fast mode. - class(integrator_lmm_ssp), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand field. - class(integrand_object), intent(inout) :: buffer !< Temporary buffer for doing fast operation. - real(R_P), intent(in) :: Dt !< Time steps. - real(R_P), intent(in) :: t(:) !< Times. - logical, optional, intent(in) :: autoupdate !< Perform cyclic autoupdate of previous time steps. - logical :: autoupdate_ !< Perform cyclic autoupdate of previous time steps, dummy var. - integer(I_P) :: s !< Steps counter. - - autoupdate_ = .true. ; if (present(autoupdate)) autoupdate_ = autoupdate - call U%multiply_fast(lhs=U, rhs=0._R_P) - do s=1, self%steps - if (self%a(s) /= 0._R_P) then - call buffer%multiply_fast(lhs=previous(s), rhs=self%a(s)) - call U%add_fast(lhs=U, rhs=buffer) - endif - if (self%b(s) /= 0._R_P) then - buffer = previous(s) - call buffer%t_fast(t=t(s)) - call buffer%multiply_fast(lhs=buffer, rhs=self%b(s)) - call U%add_fast(lhs=U, rhs=buffer) - endif - enddo - if (autoupdate_) call self%update_previous(U=U, previous=previous) - endsubroutine integrate_fast - - subroutine update_previous(self, U, previous) - !< Cyclic update previous time steps. - class(integrator_lmm_ssp), intent(in) :: self !< Integrator. - class(integrand_object), intent(in) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand field. - integer(I_P) :: s !< Steps counter. - - do s=1, self%steps - 1 - previous(s) = previous(s + 1) - enddo - previous(self%steps) = U - endsubroutine update_previous + subroutine initialize(self, scheme, autoupdate, U, stop_on_fail) + !< Create the actual LMM-SSP integrator: initialize the *a,b* coefficients. + !< + !< @note If the integrator is initialized with a bad (unsupported) number of required time steps the initialization fails and + !< the integrator error status is updated consistently for external-provided errors handling. + class(integrator_lmm_ssp), intent(inout) :: self !< Integrator. + character(*), intent(in) :: scheme !< Selected scheme. + logical, intent(in), optional :: autoupdate !< Enable cyclic autoupdate of previous time steps. + class(integrand_object), intent(in), optional :: U !< Integrand molding prototype. + logical, intent(in), optional :: stop_on_fail !< Stop execution if initialization fail. + + if (self%is_supported(scheme=scheme)) then + call self%destroy + self%description_ = trim(adjustl(scheme)) + select case(trim(adjustl(scheme))) + case('lmm_ssp_steps_3_order_2') + self%steps = 3 + allocate(self%a(1:self%steps)) ; self%a = 0.0_R_P + allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P + self%a(1) = 1._R_P/4._R_P + self%a(2) = 0._R_P + self%a(3) = 3._R_P/4._R_P + + self%b(1) = 0._R_P + self%b(2) = 0._R_P + self%b(3) = 3._R_P/2._R_P + case('lmm_ssp_steps_4_order_3') + self%steps = 4 + allocate(self%a(1:self%steps)) ; self%a = 0.0_R_P + allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P + self%a(1) = 11._R_P/27._R_P + self%a(2) = 0._R_P + self%a(3) = 0._R_P + self%a(4) = 16._R_P/27._R_P + + self%b(1) = 12._R_P/27._R_P + self%b(2) = 0._R_P + self%b(3) = 0._R_P + self%b(4) = 16._R_P/9._R_P + case('lmm_ssp_steps_5_order_3') + self%steps = 5 + allocate(self%a(1:self%steps)) ; self%a = 0.0_R_P + allocate(self%b(1:self%steps)) ; self%b = 0.0_R_P + self%a(1) = 7._R_P/32._R_P + self%a(2) = 0._R_P + self%a(3) = 0._R_P + self%a(4) = 0._R_P + self%a(5) = 25._R_P/32._R_P + + self%b(1) = 5._R_P/16._R_P + self%b(2) = 0._R_P + self%b(3) = 0._R_P + self%b(4) = 0._R_P + self%b(5) = 25._R_P/16._R_P + endselect + self%autoupdate = .true. ; if (present(autoupdate)) self%autoupdate = autoupdate + self%registers = self%steps + if (present(U)) call self%allocate_integrand_members(U=U) + else + call self%trigger_error(error=ERROR_UNSUPPORTED_SCHEME, & + error_message='"'//trim(adjustl(scheme))//'" unsupported scheme', & + is_severe=stop_on_fail) + endif + endsubroutine initialize endmodule foodie_integrator_lmm_ssp diff --git a/src/lib/foodie_integrator_lmm_ssp_vss.f90 b/src/lib/foodie_integrator_lmm_ssp_vss.f90 index 63a064e0..2f1d3680 100644 --- a/src/lib/foodie_integrator_lmm_ssp_vss.f90 +++ b/src/lib/foodie_integrator_lmm_ssp_vss.f90 @@ -39,6 +39,7 @@ module foodie_integrator_lmm_ssp_vss use foodie_error_codes, only : ERROR_UNSUPPORTED_SCHEME use foodie_integrand_object, only : integrand_object +use foodie_integrator_multistep_object, only : integrator_multistep_object use foodie_integrator_object, only : integrator_object use penf, only : I_P, R_P @@ -54,36 +55,27 @@ module foodie_integrator_lmm_ssp_vss trim(class_name_)//'_steps_5_order_3'] !< List of supported schemes. logical, parameter :: has_fast_mode_=.true. !< Flag to check if integrator provides *fast mode* integrate. -logical, parameter :: is_multistage_=.false. !< Flag to check if integrator is multistage. -logical, parameter :: is_multistep_=.true. !< Flag to check if integrator is multistep. -type, extends(integrator_object) :: integrator_lmm_ssp_vss +type, extends(integrator_multistep_object) :: integrator_lmm_ssp_vss !< FOODIE integrator: provide an explicit class of Linear Multi-step Methods (LLM) with Strong Stability Preserving property and !< variable stepsize (VSS), from 2nd to 3rd order accurate. !< !< @note The integrator must be created or initialized before used. private - integer(I_P) :: steps=0 !< Number of time steps. procedure(integrate_interface), pointer :: integrate_ => integrate_order_2 !< Integrate integrand field. procedure(integrate_fast_interface), pointer :: integrate_fast_ => integrate_order_2_fast !< Integrate integrand field, fast. contains ! deferred methods procedure, pass(self) :: class_name !< Return the class name of schemes. - procedure, pass(self) :: description !< Return pretty-printed object description. procedure, pass(self) :: has_fast_mode !< Return .true. if the integrator class has *fast mode* integrate. procedure, pass(lhs) :: integr_assign_integr !< Operator `=`. - procedure, pass(self) :: is_multistage !< Return .true. for multistage integrator. - procedure, pass(self) :: is_multistep !< Return .true. for multistep integrator. + procedure, pass(self) :: integrate !< Integrate integrand field. + procedure, pass(self) :: integrate_fast !< Integrate integrand field, fast mode. procedure, pass(self) :: is_supported !< Return .true. if the integrator class support the given scheme. - procedure, pass(self) :: stages_number !< Return number of stages used. - procedure, pass(self) :: steps_number !< Return number of steps used. procedure, pass(self) :: supported_schemes !< Return the list of supported schemes. ! public methods - procedure, pass(self) :: destroy !< Destroy the integrator. - procedure, pass(self) :: initialize !< Initialize (create) the integrator. - procedure, pass(self) :: integrate !< Integrate integrand field. - procedure, pass(self) :: integrate_fast !< Integrate integrand field, fast mode. - procedure, pass(self) :: update_previous !< Cyclic update previous time steps. + procedure, pass(self) :: destroy !< Destroy the integrator. + procedure, pass(self) :: initialize !< Initialize (create) the integrator. ! private methods procedure, pass(self), private :: integrate_order_2 !< Integrate integrand field by 2nd order formula. procedure, pass(self), private :: integrate_order_3 !< Integrate integrand field by 3rd order formula. @@ -93,27 +85,22 @@ module foodie_integrator_lmm_ssp_vss abstract interface !< Abstract interfaces of [[integrator_lmm_ssp_vss]] methods. - subroutine integrate_interface(self, U, previous, Dt, t, autoupdate) + subroutine integrate_interface(self, U, Dt, t) !< Integrate field with LMM-SSP class scheme. import :: integrand_object, integrator_lmm_ssp_vss, R_P - class(integrator_lmm_ssp_vss), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand field. - real(R_P), intent(inout) :: Dt(:) !< Time steps. - real(R_P), intent(in) :: t(:) !< Times. - logical, optional, intent(in) :: autoupdate !< Perform cyclic autoupdate of previous time steps. + class(integrator_lmm_ssp_vss), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. endsubroutine integrate_interface - subroutine integrate_fast_interface(self, U, previous, buffer, Dt, t, autoupdate) + subroutine integrate_fast_interface(self, U, Dt, t) !< Integrate field with LMM-SSP class scheme. import :: integrand_object, integrator_lmm_ssp_vss, R_P - class(integrator_lmm_ssp_vss), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand field. - class(integrand_object), intent(inout) :: buffer !< Temporary buffer for doing fast operation. - real(R_P), intent(inout) :: Dt(:) !< Time steps. - real(R_P), intent(in) :: t(:) !< Times. - logical, optional, intent(in) :: autoupdate !< Perform cyclic autoupdate of previous time steps. + class(integrator_lmm_ssp_vss), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. endsubroutine integrate_fast_interface endinterface @@ -127,25 +114,6 @@ pure function class_name(self) class_name = trim(adjustl(class_name_)) endfunction class_name - pure function description(self, prefix) result(desc) - !< Return a pretty-formatted object description. - class(integrator_lmm_ssp_vss), intent(in) :: self !< Integrator. - character(*), intent(in), optional :: prefix !< Prefixing string. - character(len=:), allocatable :: desc !< Description. - character(len=:), allocatable :: prefix_ !< Prefixing string, local variable. - character(len=1), parameter :: NL=new_line('a') !< New line character. - integer(I_P) :: s !< Counter. - - prefix_ = '' ; if (present(prefix)) prefix_ = prefix - desc = '' - desc = desc//prefix_//'Strong Stability preserving Linear-Multistep-Methods Variable Stepsize class'//NL - desc = desc//prefix_//' Supported schemes:'//NL - do s=lbound(supported_schemes_, dim=1), ubound(supported_schemes_, dim=1) - 1 - desc = desc//prefix_//' + '//supported_schemes_(s)//NL - enddo - desc = desc//prefix_//' + '//supported_schemes_(ubound(supported_schemes_, dim=1)) - endfunction description - elemental function has_fast_mode(self) !< Return .true. if the integrator class has *fast mode* integrate. class(integrator_lmm_ssp_vss), intent(in) :: self !< Integrator. @@ -159,29 +127,32 @@ pure subroutine integr_assign_integr(lhs, rhs) class(integrator_lmm_ssp_vss), intent(inout) :: lhs !< Left hand side. class(integrator_object), intent(in) :: rhs !< Right hand side. - call lhs%assign_abstract(rhs=rhs) + call lhs%assign_multistep(rhs=rhs) select type(rhs) class is (integrator_lmm_ssp_vss) - lhs%steps = rhs%steps if (associated(rhs%integrate_)) lhs%integrate_ => rhs%integrate_ endselect endsubroutine integr_assign_integr - elemental function is_multistage(self) - !< Return .true. for multistage integrator. - class(integrator_lmm_ssp_vss), intent(in) :: self !< Integrator. - logical :: is_multistage !< Inquire result. + subroutine integrate(self, U, Dt, t) + !< Integrate field with LMM-SSP class scheme. + class(integrator_lmm_ssp_vss), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. - is_multistage = is_multistage_ - endfunction is_multistage + call self%integrate_(U=U, Dt=Dt, t=t) + endsubroutine integrate - elemental function is_multistep(self) - !< Return .true. for multistage integrator. - class(integrator_lmm_ssp_vss), intent(in) :: self !< Integrator. - logical :: is_multistep !< Inquire result. + subroutine integrate_fast(self, U, Dt, t) + !< Integrate field with LMM-SSP class scheme, fast mode. + class(integrator_lmm_ssp_vss), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. - is_multistep = is_multistep_ - endfunction is_multistep + call self%integrate_fast_(U=U, Dt=Dt, t=t) + endsubroutine integrate_fast elemental function is_supported(self, scheme) !< Return .true. if the integrator class support the given scheme. @@ -199,22 +170,6 @@ elemental function is_supported(self, scheme) enddo endfunction is_supported - elemental function stages_number(self) - !< Return number of stages used. - class(integrator_lmm_ssp_vss), intent(in) :: self !< Integrator. - integer(I_P) :: stages_number !< Number of stages used. - - stages_number = 0 - endfunction stages_number - - elemental function steps_number(self) - !< Return number of steps used. - class(integrator_lmm_ssp_vss), intent(in) :: self !< Integrator. - integer(I_P) :: steps_number !< Number of steps used. - - steps_number = self%steps - endfunction steps_number - pure function supported_schemes(self) result(schemes) !< Return the list of supported schemes. class(integrator_lmm_ssp_vss), intent(in) :: self !< Integrator. @@ -229,185 +184,132 @@ elemental subroutine destroy(self) !< Destroy the integrator. class(integrator_lmm_ssp_vss), intent(inout) :: self !< Integrator. - call self%destroy_abstract - self%steps = 0 + call self%destroy_multistep self%integrate_ => integrate_order_2 endsubroutine destroy - subroutine initialize(self, scheme, stop_on_fail) + subroutine initialize(self, scheme, autoupdate, U, stop_on_fail) !< Create the actual LMM-SSP-VSS integrator. !< !< @note If the integrator is initialized with a bad (unsupported) number of required time steps the initialization fails and !< the integrator error status is updated consistently for external-provided errors handling. class(integrator_lmm_ssp_vss), intent(inout) :: self !< Integrator. character(*), intent(in) :: scheme !< Selected scheme. + logical, intent(in), optional :: autoupdate !< Enable cyclic autoupdate of previous time steps. + class(integrand_object), intent(in), optional :: U !< Integrand molding prototype. logical, intent(in), optional :: stop_on_fail !< Stop execution if initialization fail. if (self%is_supported(scheme=scheme)) then - call self%destroy - select case(trim(adjustl(scheme))) - case('lmm_ssp_vss_steps_2_order_2') - self%steps = 2 - self%integrate_ => integrate_order_2 - self%integrate_fast_ => integrate_order_2_fast - case('lmm_ssp_vss_steps_3_order_2') - self%steps = 3 - self%integrate_ => integrate_order_2 - self%integrate_fast_ => integrate_order_2_fast - case('lmm_ssp_vss_steps_3_order_3') - self%steps = 3 - self%integrate_ => integrate_order_3 - self%integrate_fast_ => integrate_order_3_fast - case('lmm_ssp_vss_steps_4_order_3') - self%steps = 4 - self%integrate_ => integrate_order_3 - self%integrate_fast_ => integrate_order_3_fast - case('lmm_ssp_vss_steps_5_order_3') - self%steps = 5 - self%integrate_ => integrate_order_3 - self%integrate_fast_ => integrate_order_3_fast - endselect + call self%destroy + self%description_ = trim(adjustl(scheme)) + select case(trim(adjustl(scheme))) + case('lmm_ssp_vss_steps_2_order_2') + self%steps = 2 + self%integrate_ => integrate_order_2 + self%integrate_fast_ => integrate_order_2_fast + case('lmm_ssp_vss_steps_3_order_2') + self%steps = 3 + self%integrate_ => integrate_order_2 + self%integrate_fast_ => integrate_order_2_fast + case('lmm_ssp_vss_steps_3_order_3') + self%steps = 3 + self%integrate_ => integrate_order_3 + self%integrate_fast_ => integrate_order_3_fast + case('lmm_ssp_vss_steps_4_order_3') + self%steps = 4 + self%integrate_ => integrate_order_3 + self%integrate_fast_ => integrate_order_3_fast + case('lmm_ssp_vss_steps_5_order_3') + self%steps = 5 + self%integrate_ => integrate_order_3 + self%integrate_fast_ => integrate_order_3_fast + endselect + self%autoupdate = .true. ; if (present(autoupdate)) self%autoupdate = autoupdate + self%registers = self%steps + if (present(U)) call self%allocate_integrand_members(U=U) else - call self%trigger_error(error=ERROR_UNSUPPORTED_SCHEME, & - error_message='"'//trim(adjustl(scheme))//'" unsupported scheme', & - is_severe=stop_on_fail) + call self%trigger_error(error=ERROR_UNSUPPORTED_SCHEME, & + error_message='"'//trim(adjustl(scheme))//'" unsupported scheme', & + is_severe=stop_on_fail) endif endsubroutine initialize - subroutine integrate(self, U, previous, Dt, t, autoupdate) - !< Integrate field with LMM-SSP class scheme. - class(integrator_lmm_ssp_vss), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand field. - real(R_P), intent(inout) :: Dt(:) !< Time steps. - real(R_P), intent(in) :: t(:) !< Times. - logical, optional, intent(in) :: autoupdate !< Perform cyclic autoupdate of previous time steps. - - call self%integrate_(U=U, previous=previous, Dt=Dt, t=t, autoupdate=autoupdate) - endsubroutine integrate - - subroutine integrate_fast(self, U, previous, buffer, Dt, t, autoupdate) - !< Integrate field with LMM-SSP class scheme. - class(integrator_lmm_ssp_vss), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand field. - class(integrand_object), intent(inout) :: buffer !< Temporary buffer for doing fast operation. - real(R_P), intent(inout) :: Dt(:) !< Time steps. - real(R_P), intent(in) :: t(:) !< Times. - logical, optional, intent(in) :: autoupdate !< Perform cyclic autoupdate of previous time steps. - - call self%integrate_fast_(U=U, previous=previous, buffer=buffer, Dt=Dt, t=t, autoupdate=autoupdate) - endsubroutine integrate_fast - - subroutine update_previous(self, U, previous, Dt) - !< Cyclic update previous time steps. - class(integrator_lmm_ssp_vss), intent(in) :: self !< Integrator. - class(integrand_object), intent(in) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand field. - real(R_P), intent(inout) :: Dt(:) !< Time steps. - integer(I_P) :: s !< Steps counter. - - do s=1, self%steps - 1 - previous(s) = previous(s + 1) - Dt(s) = Dt(s + 1) - enddo - previous(self%steps) = U - endsubroutine update_previous - ! private methods - subroutine integrate_order_2(self, U, previous, Dt, t, autoupdate) + subroutine integrate_order_2(self, U, Dt, t) !< Integrate field with LMM-SSP-VSS 2nd order class scheme. - class(integrator_lmm_ssp_vss), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand field. - real(R_P), intent(inout) :: Dt(:) !< Time steps. - real(R_P), intent(in) :: t(:) !< Times. - logical, optional, intent(in) :: autoupdate !< Perform cyclic autoupdate of previous time steps. - logical :: autoupdate_ !< Perform cyclic autoupdate of previous time steps, dummy var. - real(R_P) :: omega_ !< Omega coefficient. - real(R_P) :: omega_sq !< Square of omega coefficient. - - autoupdate_ = .true. ; if (present(autoupdate)) autoupdate_ = autoupdate - omega_ = omega(Dt=Dt, s=self%steps-1) + class(integrator_lmm_ssp_vss), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + real(R_P) :: omega_ !< Omega coefficient. + real(R_P) :: omega_sq !< Square of omega coefficient. + + omega_ = omega(Dt=self%Dt, s=self%steps-1) omega_sq = omega_ * omega_ - U = (previous(1) * (1._R_P / omega_sq)) + (previous(self%steps) * ((omega_sq - 1._R_P) / omega_sq)) + & - (previous(self%steps)%t(t=t(self%steps)) * (Dt(self%steps) * (omega_ + 1._R_P) / omega_)) - if (autoupdate_) call self%update_previous(U=U, previous=previous, Dt=Dt) + U = (self%previous(1) * (1._R_P / omega_sq)) + (self%previous(self%steps) * ((omega_sq - 1._R_P) / omega_sq)) + & + (self%previous(self%steps)%t(t=self%t(self%steps)) * (self%Dt(self%steps) * (omega_ + 1._R_P) / omega_)) + if (self%autoupdate) call self%update_previous(U=U, previous=self%previous, Dt=Dt, t=t, previous_Dt=self%Dt, previous_t=self%t) endsubroutine integrate_order_2 - subroutine integrate_order_3(self, U, previous, Dt, t, autoupdate) + subroutine integrate_order_3(self, U, Dt, t) !< Integrate field with LMM-SSP-VSS 3rd order class scheme. - class(integrator_lmm_ssp_vss), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand field. - real(R_P), intent(inout) :: Dt(:) !< Time steps. - real(R_P), intent(in) :: t(:) !< Times. - logical, optional, intent(in) :: autoupdate !< Perform cyclic autoupdate of previous time steps. - logical :: autoupdate_ !< Perform cyclic autoupdate of previous time steps, dummy var. - real(R_P) :: omega_ !< Omega coefficient. - - autoupdate_ = .true. ; if (present(autoupdate)) autoupdate_ = autoupdate - omega_= omega(Dt=Dt, s=self%steps-1) - U = (previous(1) * ((3._R_P * omega_ + 2._R_P) / omega_ ** 3 )) + & - (previous(self%steps) * (((omega_ + 1._R_P) ** 2) * (omega_ - 2._R_P) / omega_ ** 3)) + & - (previous(1)%t(t=t(1)) * (Dt(self%steps) * (omega_ + 1._R_P) / omega_ ** 2)) + & - (previous(self%steps)%t(t=t(self%steps)) * (Dt(self%steps) * (omega_ + 1._R_P) ** 2 / omega_ ** 2)) - if (autoupdate_) call self%update_previous(U=U, previous=previous, Dt=Dt) + class(integrator_lmm_ssp_vss), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + real(R_P) :: omega_ !< Omega coefficient. + + omega_= omega(Dt=self%Dt, s=self%steps-1) + U = (self%previous(1) * ((3._R_P * omega_ + 2._R_P) / omega_ ** 3 )) + & + (self%previous(self%steps) * (((omega_ + 1._R_P) ** 2) * (omega_ - 2._R_P) / omega_ ** 3)) + & + (self%previous(1)%t(t=self%t(1)) * (self%Dt(self%steps) * (omega_ + 1._R_P) / omega_ ** 2)) + & + (self%previous(self%steps)%t(t=self%t(self%steps)) * (self%Dt(self%steps) * (omega_ + 1._R_P) ** 2 / omega_ ** 2)) + if (self%autoupdate) call self%update_previous(U=U, previous=self%previous, Dt=Dt, t=t, previous_Dt=self%Dt, previous_t=self%t) endsubroutine integrate_order_3 - subroutine integrate_order_2_fast(self, U, previous, buffer, Dt, t, autoupdate) - !< Integrate field with LMM-SSP-VSS 2nd order class scheme. - class(integrator_lmm_ssp_vss), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand field. - class(integrand_object), intent(inout) :: buffer !< Temporary buffer for doing fast operation. - real(R_P), intent(inout) :: Dt(:) !< Time steps. - real(R_P), intent(in) :: t(:) !< Times. - logical, optional, intent(in) :: autoupdate !< Perform cyclic autoupdate of previous time steps. - logical :: autoupdate_ !< Perform cyclic autoupdate of previous time steps, dummy var. - real(R_P) :: omega_ !< Omega coefficient. - real(R_P) :: omega_sq !< Square of omega coefficient. - - autoupdate_ = .true. ; if (present(autoupdate)) autoupdate_ = autoupdate - omega_ = omega(Dt=Dt, s=self%steps-1) + subroutine integrate_order_2_fast(self, U, Dt, t) + !< Integrate field with LMM-SSP-VSS 2nd order class scheme, fast mode. + class(integrator_lmm_ssp_vss), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + real(R_P) :: omega_ !< Omega coefficient. + real(R_P) :: omega_sq !< Square of omega coefficient. + + omega_ = omega(Dt=self%Dt, s=self%steps-1) omega_sq = omega_ * omega_ - call U%multiply_fast(lhs=previous(1), rhs=1._R_P / omega_sq) - call buffer%multiply_fast(lhs=previous(self%steps), rhs=(omega_sq - 1._R_P) / omega_sq) - call U%add_fast(lhs=U, rhs=buffer) - buffer = previous(self%steps) - call buffer%t_fast(t=t(self%steps)) - call buffer%multiply_fast(lhs=buffer, rhs=Dt(self%steps) * (omega_ + 1._R_P) / omega_) - call U%add_fast(lhs=U, rhs=buffer) - if (autoupdate_) call self%update_previous(U=U, previous=previous, Dt=Dt) + call U%multiply_fast(lhs=self%previous(1), rhs=1._R_P / omega_sq) + call self%buffer%multiply_fast(lhs=self%previous(self%steps), rhs=(omega_sq - 1._R_P) / omega_sq) + call U%add_fast(lhs=U, rhs=self%buffer) + self%buffer = self%previous(self%steps) + call self%buffer%t_fast(t=self%t(self%steps)) + call self%buffer%multiply_fast(lhs=self%buffer, rhs=self%Dt(self%steps) * (omega_ + 1._R_P) / omega_) + call U%add_fast(lhs=U, rhs=self%buffer) + if (self%autoupdate) call self%update_previous(U=U, previous=self%previous, Dt=Dt, t=t, previous_Dt=self%Dt, previous_t=self%t) endsubroutine integrate_order_2_fast - subroutine integrate_order_3_fast(self, U, previous, buffer, Dt, t, autoupdate) + subroutine integrate_order_3_fast(self, U, Dt, t) !< Integrate field with LMM-SSP-VSS 3rd order class scheme, fast mode. - class(integrator_lmm_ssp_vss), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand field. - class(integrand_object), intent(inout) :: buffer !< Temporary buffer for doing fast operation. - real(R_P), intent(inout) :: Dt(:) !< Time steps. - real(R_P), intent(in) :: t(:) !< Times. - logical, optional, intent(in) :: autoupdate !< Perform cyclic autoupdate of previous time steps. - logical :: autoupdate_ !< Perform cyclic autoupdate of previous time steps, dummy var. - real(R_P) :: omega_ !< Omega coefficient. - - autoupdate_ = .true. ; if (present(autoupdate)) autoupdate_ = autoupdate - omega_= omega(Dt=Dt, s=self%steps-1) - - call U%multiply_fast(lhs=previous(1), rhs=(3._R_P * omega_ + 2._R_P) / (omega_ ** 3)) - call buffer%multiply_fast(lhs=previous(self%steps), rhs=(((omega_ + 1._R_P) ** 2) * (omega_ - 2._R_P) / (omega_ ** 3))) - call U%add_fast(lhs=U, rhs=buffer) - buffer = previous(1) - call buffer%t_fast(t=t(1)) - call buffer%multiply_fast(lhs=buffer, rhs=Dt(self%steps) * (omega_ + 1._R_P) / (omega_ ** 2)) - call U%add_fast(lhs=U, rhs=buffer) - buffer = previous(self%steps) - call buffer%t_fast(t=t(self%steps)) - call buffer%multiply_fast(lhs=buffer, rhs=(Dt(self%steps) * (omega_ + 1._R_P) ** 2 / (omega_ ** 2))) - call U%add_fast(lhs=U, rhs=buffer) - if (autoupdate_) call self%update_previous(U=U, previous=previous, Dt=Dt) + class(integrator_lmm_ssp_vss), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + real(R_P) :: omega_ !< Omega coefficient. + + omega_= omega(Dt=self%Dt, s=self%steps-1) + + call U%multiply_fast(lhs=self%previous(1), rhs=(3._R_P * omega_ + 2._R_P) / (omega_ ** 3)) + call self%buffer%multiply_fast(lhs=self%previous(self%steps), rhs=(((omega_ + 1._R_P) ** 2) * (omega_ - 2._R_P) / (omega_ ** 3))) + call U%add_fast(lhs=U, rhs=self%buffer) + self%buffer = self%previous(1) + call self%buffer%t_fast(t=self%t(1)) + call self%buffer%multiply_fast(lhs=self%buffer, rhs=self%Dt(self%steps) * (omega_ + 1._R_P) / (omega_ ** 2)) + call U%add_fast(lhs=U, rhs=self%buffer) + self%buffer = self%previous(self%steps) + call self%buffer%t_fast(t=self%t(self%steps)) + call self%buffer%multiply_fast(lhs=self%buffer, rhs=(self%Dt(self%steps) * (omega_ + 1._R_P) ** 2 / (omega_ ** 2))) + call U%add_fast(lhs=U, rhs=self%buffer) + if (self%autoupdate) call self%update_previous(U=U, previous=self%previous, Dt=Dt, t=t, previous_Dt=self%Dt, previous_t=self%t) endsubroutine integrate_order_3_fast ! private non TBP diff --git a/src/lib/foodie_integrator_ms_runge_kutta_ssp.f90 b/src/lib/foodie_integrator_ms_runge_kutta_ssp.f90 index 9b649398..02a376a8 100644 --- a/src/lib/foodie_integrator_ms_runge_kutta_ssp.f90 +++ b/src/lib/foodie_integrator_ms_runge_kutta_ssp.f90 @@ -36,6 +36,7 @@ module foodie_integrator_ms_runge_kutta_ssp use foodie_error_codes, only : ERROR_UNSUPPORTED_SCHEME use foodie_integrand_object, only : integrand_object +use foodie_integrator_multistage_multistep_object, only : integrator_multistage_multistep_object use foodie_integrator_object, only : integrator_object use penf, only : I_P, R_P @@ -50,41 +51,31 @@ module foodie_integrator_ms_runge_kutta_ssp !< schemes. logical, parameter :: has_fast_mode_=.true. !< Flag to check if integrator provides *fast mode* integrate. -logical, parameter :: is_multistage_=.true. !< Flag to check if integrator is multistage. -logical, parameter :: is_multistep_=.true. !< Flag to check if integrator is multistep. -type, extends(integrator_object) :: integrator_ms_runge_kutta_ssp +type, extends(integrator_multistage_multistep_object) :: integrator_ms_runge_kutta_ssp !< FOODIE integrator: provide an explicit class of Multi-step Runge-Kutta Methods with Strong Stability Preserving property, !< from 3rd to 8th order accurate. !< !< @note The integrator must be created or initialized (initialize the *A,Ahat,B,Bhat,D,T* coefficients) before used. private - integer(I_P) :: steps=0 !< Number of time steps. - integer(I_P) :: stages=0 !< Number of stages. real(R_P), allocatable :: A(:,:) !< *A* coefficients. real(R_P), allocatable :: Ahat(:,:) !< *Ahat* coefficients. real(R_P), allocatable :: B(:) !< *B* coefficients. real(R_P), allocatable :: Bhat(:) !< *Bhat* coefficients. real(R_P), allocatable :: D(:,:) !< *D* coefficients. - real(R_P), allocatable :: T(:) !< *T* coefficients. + real(R_P), allocatable :: Q(:) !< *T* coefficients. contains ! deferred methods procedure, pass(self) :: class_name !< Return the class name of schemes. - procedure, pass(self) :: description !< Return pretty-printed object description. procedure, pass(self) :: has_fast_mode !< Return .true. if the integrator class has *fast mode* integrate. procedure, pass(lhs) :: integr_assign_integr !< Operator `=`. - procedure, pass(self) :: is_multistage !< Return .true. for multistage integrator. - procedure, pass(self) :: is_multistep !< Return .true. for multistep integrator. + procedure, pass(self) :: integrate !< Integrate integrand field. + procedure, pass(self) :: integrate_fast !< Integrate integrand field, fast mode. procedure, pass(self) :: is_supported !< Return .true. if the integrator class support the given scheme. - procedure, pass(self) :: stages_number !< Return number of stages used. - procedure, pass(self) :: steps_number !< Return number of steps used. procedure, pass(self) :: supported_schemes !< Return the list of supported schemes. ! public methods - procedure, pass(self) :: destroy !< Destroy the integrator. - procedure, pass(self) :: initialize !< Initialize (create) the integrator. - procedure, pass(self) :: integrate !< Integrate integrand field. - procedure, pass(self) :: integrate_fast !< Integrate integrand field, fast mode. - procedure, pass(self) :: update_previous !< Cyclic update previous time steps. + procedure, pass(self) :: destroy !< Destroy the integrator. + procedure, pass(self) :: initialize !< Initialize (create) the integrator. endtype integrator_ms_runge_kutta_ssp contains @@ -97,25 +88,6 @@ pure function class_name(self) class_name = trim(adjustl(class_name_)) endfunction class_name - pure function description(self, prefix) result(desc) - !< Return a pretty-formatted object description. - class(integrator_ms_runge_kutta_ssp), intent(in) :: self !< Integrator. - character(*), intent(in), optional :: prefix !< Prefixing string. - character(len=:), allocatable :: desc !< Description. - character(len=:), allocatable :: prefix_ !< Prefixing string, local variable. - character(len=1), parameter :: NL=new_line('a') !< New line character. - integer(I_P) :: s !< Counter. - - prefix_ = '' ; if (present(prefix)) prefix_ = prefix - desc = '' - desc = desc//prefix_//'Multi-step Strong Stability preserving Runge-Kutta schemes class'//NL - desc = desc//prefix_//' Supported schemes:'//NL - do s=lbound(supported_schemes_, dim=1), ubound(supported_schemes_, dim=1) - 1 - desc = desc//prefix_//' + '//supported_schemes_(s)//NL - enddo - desc = desc//prefix_//' + '//supported_schemes_(ubound(supported_schemes_, dim=1)) - endfunction description - elemental function has_fast_mode(self) !< Return .true. if the integrator class has *fast mode* integrate. class(integrator_ms_runge_kutta_ssp), intent(in) :: self !< Integrator. @@ -129,35 +101,118 @@ pure subroutine integr_assign_integr(lhs, rhs) class(integrator_ms_runge_kutta_ssp), intent(inout) :: lhs !< Left hand side. class(integrator_object), intent(in) :: rhs !< Right hand side. - call lhs%assign_abstract(rhs=rhs) + call lhs%assign_multistage_multistep(rhs=rhs) select type(rhs) class is (integrator_ms_runge_kutta_ssp) - lhs%steps = rhs%steps - lhs%stages = rhs%stages - if (allocated(rhs%A )) lhs%A = rhs%A - if (allocated(rhs%Ahat)) lhs%Ahat = rhs%Ahat - if (allocated(rhs%B )) lhs%B = rhs%B - if (allocated(rhs%Bhat)) lhs%Bhat = rhs%Bhat - if (allocated(rhs%D )) lhs%D = rhs%D - if (allocated(rhs%T )) lhs%T = rhs%T + if (allocated(rhs%A )) lhs%A = rhs%A + if (allocated(rhs%Ahat)) lhs%Ahat = rhs%Ahat + if (allocated(rhs%B )) lhs%B = rhs%B + if (allocated(rhs%Bhat)) lhs%Bhat = rhs%Bhat + if (allocated(rhs%D )) lhs%D = rhs%D + if (allocated(rhs%Q )) lhs%Q = rhs%Q endselect endsubroutine integr_assign_integr - elemental function is_multistage(self) - !< Return .true. for multistage integrator. - class(integrator_ms_runge_kutta_ssp), intent(in) :: self !< Integrator. - logical :: is_multistage !< Inquire result. + subroutine integrate(self, U, Dt, t) + !< Integrate field with LMM-SSP class scheme. + class(integrator_ms_runge_kutta_ssp), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + integer(I_P) :: k, kk !< Stages counters. + integer(I_P) :: s !< Steps counter. - is_multistage = is_multistage_ - endfunction is_multistage + ! computing stages + self%stage(1) = U + do k=2, self%stages + self%stage(k) = U * 0._R_P + do s=1, self%steps + if (self%D(k, s) /= 0._R_P) self%stage(k) = self%stage(k) + (self%previous(s) * self%D(k, s)) + enddo + do s=1, self%steps - 1 + if (self%Ahat(k, s) /= 0._R_P) self%stage(k) = self%stage(k) + (self%previous(s)%t(t=self%t(s)) * (Dt * self%Ahat(k, s))) + enddo + do kk=1, k - 1 + if (self%A(k, kk) /= 0._R_P) self%stage(k) = self%stage(k) + (self%stage(kk)%t(t=t) * (Dt * self%A(k, kk))) + enddo + enddo + ! computing new time step + U = U * 0._R_P + do s=1, self%steps + if (self%Q(s) /= 0._R_P) U = U + (self%previous(s) * self%Q(s)) + enddo + do s=1, self%steps - 1 + if (self%Bhat(s) /= 0._R_P) U = U + (self%previous(s)%t(t=self%t(s)) * (Dt * self%Bhat(s))) + enddo + do k=1, self%stages + if (self%B(k) /= 0._R_P) U = U + (self%stage(k)%t(t=t) * (Dt * self%B(k))) + enddo + if (self%autoupdate) call self%update_previous(U=U, previous=self%previous, Dt=Dt, t=t, previous_t=self%t) + endsubroutine integrate - elemental function is_multistep(self) - !< Return .true. for multistage integrator. - class(integrator_ms_runge_kutta_ssp), intent(in) :: self !< Integrator. - logical :: is_multistep !< Inquire result. + subroutine integrate_fast(self, U, Dt, t) + !< Integrate field with LMM-SSP class scheme, fast mode. + class(integrator_ms_runge_kutta_ssp), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + integer(I_P) :: k, kk !< Stages counters. + integer(I_P) :: s !< Steps counter. - is_multistep = is_multistep_ - endfunction is_multistep + ! computing stages + self%stage(1) = U + do k=2, self%stages + call self%stage(k)%multiply_fast(lhs=U, rhs=0._R_P) + do s=1, self%steps + if (self%D(k, s) /= 0._R_P) then + call self%buffer%multiply_fast(lhs=self%previous(s), rhs=self%D(k, s)) + call self%stage(k)%add_fast(lhs=self%stage(k), rhs=self%buffer) + endif + enddo + do s=1, self%steps - 1 + if (self%Ahat(k, s) /= 0._R_P) then + self%buffer = self%previous(s) + call self%buffer%t_fast(t=self%t(s)) + call self%buffer%multiply_fast(lhs=self%buffer, rhs=Dt * self%Ahat(k, s)) + call self%stage(k)%add_fast(lhs=self%stage(k), rhs=self%buffer) + endif + enddo + do kk=1, k - 1 + if (self%A(k, kk) /= 0._R_P) then + self%buffer = self%stage(kk) + call self%buffer%t_fast(t=t) + call self%buffer%multiply_fast(lhs=self%buffer, rhs=Dt * self%A(k, kk)) + call self%stage(k)%add_fast(lhs=self%stage(k), rhs=self%buffer) + endif + enddo + enddo + ! computing new time step + U = U * 0._R_P + do s=1, self%steps + if (self%Q(s) /= 0._R_P) then + call self%buffer%multiply_fast(lhs=self%previous(s), rhs=self%Q(s)) + call U%add_fast(lhs=U, rhs=self%buffer) + endif + enddo + do s=1, self%steps - 1 + if (self%Bhat(s) /= 0._R_P) then + self%buffer = self%previous(s) + call self%buffer%t_fast(t=self%t(s)) + call self%buffer%multiply_fast(lhs=self%buffer, rhs=Dt * self%Bhat(s)) + call U%add_fast(lhs=U, rhs=self%buffer) + endif + enddo + do k=1, self%stages + if (self%B(k) /= 0._R_P) U = U + (self%stage(k)%t(t=t) * (Dt * self%B(k))) + if (self%B(k) /= 0._R_P) then + self%buffer = self%stage(k) + call self%buffer%t_fast(t=t) + call self%buffer%multiply_fast(lhs=self%buffer, rhs=Dt * self%B(k)) + call U%add_fast(lhs=U, rhs=self%buffer) + endif + enddo + if (self%autoupdate) call self%update_previous(U=U, previous=self%previous, Dt=Dt, t=t, previous_t=self%t) + endsubroutine integrate_fast elemental function is_supported(self, scheme) !< Return .true. if the integrator class support the given scheme. @@ -175,22 +230,6 @@ elemental function is_supported(self, scheme) enddo endfunction is_supported - elemental function stages_number(self) - !< Return number of stages used. - class(integrator_ms_runge_kutta_ssp), intent(in) :: self !< Integrator. - integer(I_P) :: stages_number !< Number of stages used. - - stages_number = self%stages - endfunction stages_number - - elemental function steps_number(self) - !< Return number of steps used. - class(integrator_ms_runge_kutta_ssp), intent(in) :: self !< Integrator. - integer(I_P) :: steps_number !< Number of steps used. - - steps_number = self%steps - endfunction steps_number - pure function supported_schemes(self) result(schemes) !< Return the list of supported schemes. class(integrator_ms_runge_kutta_ssp), intent(in) :: self !< Integrator. @@ -205,25 +244,27 @@ elemental subroutine destroy(self) !< Destroy the integrator. class(integrator_ms_runge_kutta_ssp), intent(inout) :: self !< Integrator. - call self%destroy_abstract - self%steps = 0 - self%stages = 0 + call self%destroy_multistage_multistep if (allocated(self%A )) deallocate(self%A ) if (allocated(self%Ahat)) deallocate(self%Ahat) if (allocated(self%B )) deallocate(self%B ) if (allocated(self%Bhat)) deallocate(self%Bhat) if (allocated(self%D )) deallocate(self%D ) - if (allocated(self%T )) deallocate(self%T ) + if (allocated(self%Q )) deallocate(self%Q ) endsubroutine destroy - subroutine initialize(self, scheme, stop_on_fail) + subroutine initialize(self, scheme, iterations, autoupdate, U, stop_on_fail) !< Create the actual MS-RK-SSP integrator: initialize the *A,Ahat,B,Bhat,D,T* coefficients. class(integrator_ms_runge_kutta_ssp), intent(inout) :: self !< Integrator. character(*), intent(in) :: scheme !< Selected scheme. + integer(I_P), intent(in), optional :: iterations !< Implicit iterations. + logical, intent(in), optional :: autoupdate !< Enable cyclic autoupdate of previous time steps. + class(integrand_object), intent(in), optional :: U !< Integrand molding prototype. logical, intent(in), optional :: stop_on_fail !< Stop execution if initialization fail. if (self%is_supported(scheme=scheme)) then call self%destroy + self%description_ = trim(adjustl(scheme)) select case(trim(adjustl(scheme))) case('ms_runge_kutta_ssp_steps_2_stages_2_order_3') self%steps = 2 @@ -246,9 +287,9 @@ subroutine initialize(self, scheme, stop_on_fail) self%D(2, 1) = 1._R_P / 3._R_P self%D(2, 2) = 2._R_P / 3._R_P - allocate(self%T(1:self%steps)) ; self%T = 0._R_P - self%T(1) = 0.607695154586736_R_P - self%T(2) = 0.392304845413264_R_P + allocate(self%Q(1:self%steps)) ; self%Q = 0._R_P + self%Q(1) = 0.607695154586736_R_P + self%Q(2) = 0.392304845413264_R_P case('ms_runge_kutta_ssp_steps_3_stages_2_order_3') self%steps = 3 @@ -272,10 +313,10 @@ subroutine initialize(self, scheme, stop_on_fail) self%D(2, 2) = 1.80945758995975e-24_R_P self%D(2, 3) = 0.813566151883148_R_P - allocate(self%T(1:self%steps)) ; self%T = 0._R_P - self%T(1) = 0.312198313277933_R_P - self%T(2) = 2.58493941422821e-24_R_P - self%T(3) = 0.687801686722067_R_P + allocate(self%Q(1:self%steps)) ; self%Q = 0._R_P + self%Q(1) = 0.312198313277933_R_P + self%Q(2) = 2.58493941422821e-24_R_P + self%Q(3) = 0.687801686722067_R_P case('ms_runge_kutta_ssp_steps_4_stages_5_order_8') self%steps = 4 @@ -344,143 +385,21 @@ subroutine initialize(self, scheme, stop_on_fail) self%D(4, 4) = 0.0749075156298171_R_P self%D(5, 4) = 0.353491551483958_R_P - allocate(self%T(1:self%steps)) ; self%T = 0._R_P - self%T(1) = 0.0273988434707855_R_P - self%T(2) = 0.286296288278021_R_P - self%T(3) = 0.484893800452111_R_P - self%T(4) = 0.201411067799082_R_P + allocate(self%Q(1:self%steps)) ; self%Q = 0._R_P + self%Q(1) = 0.0273988434707855_R_P + self%Q(2) = 0.286296288278021_R_P + self%Q(3) = 0.484893800452111_R_P + self%Q(4) = 0.201411067799082_R_P endselect + self%autoupdate = .true. ; if (present(autoupdate)) self%autoupdate = autoupdate + self%iterations = 1 ; if (present(iterations)) self%iterations = iterations + self%registers_stages = self%stages + self%registers_steps = self%steps + if (present(U)) call self%allocate_integrand_members(U=U) else call self%trigger_error(error=ERROR_UNSUPPORTED_SCHEME, & error_message='"'//trim(adjustl(scheme))//'" unsupported scheme', & is_severe=stop_on_fail) endif endsubroutine initialize - - subroutine integrate(self, U, previous, stage, Dt, t, autoupdate) - !< Integrate field with LMM-SSP class scheme. - class(integrator_ms_runge_kutta_ssp), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand. - class(integrand_object), intent(inout) :: stage(1:) !< Runge-Kutta stages [1:stages]. - real(R_P), intent(in) :: Dt !< Time steps. - real(R_P), intent(in) :: t(:) !< Times. - logical, intent(in), optional :: autoupdate !< Perform cyclic autoupdate of previous steps. - logical :: autoupdate_ !< Perform cyclic autoupdate of previous steps, - !< local variable. - integer(I_P) :: k, kk !< Stages counters. - integer(I_P) :: s !< Steps counter. - - autoupdate_ = .true. ; if (present(autoupdate)) autoupdate_ = autoupdate - ! computing stages - stage(1) = U - do k=2, self%stages - stage(k) = U * 0._R_P - do s=1, self%steps - if (self%D(k, s) /= 0._R_P) stage(k) = stage(k) + (previous(s) * self%D(k, s)) - enddo - do s=1, self%steps - 1 - if (self%Ahat(k, s) /= 0._R_P) stage(k) = stage(k) + (previous(s)%t(t=t(s)) * (Dt * self%Ahat(k, s))) - enddo - do kk=1, k - 1 - if (self%A(k, kk) /= 0._R_P) stage(k) = stage(k) + (stage(kk)%t(t=t(self%steps)) * (Dt * self%A(k, kk))) - enddo - enddo - ! computing new time step - U = U * 0._R_P - do s=1, self%steps - if (self%T(s) /= 0._R_P) U = U + (previous(s) * self%T(s)) - enddo - do s=1, self%steps - 1 - if (self%Bhat(s) /= 0._R_P) U = U + (previous(s)%t(t=t(s)) * (Dt * self%Bhat(s))) - enddo - do k=1, self%stages - if (self%B(k) /= 0._R_P) U = U + (stage(k)%t(t=t(self%steps)) * (Dt * self%B(k))) - enddo - if (autoupdate_) call self%update_previous(U=U, previous=previous) - endsubroutine integrate - - subroutine integrate_fast(self, U, previous, stage, buffer, Dt, t, autoupdate) - !< Integrate field with LMM-SSP class scheme, fast mode. - class(integrator_ms_runge_kutta_ssp), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand. - class(integrand_object), intent(inout) :: stage(1:) !< Runge-Kutta stages [1:stages]. - class(integrand_object), intent(inout) :: buffer !< Temporary buffer for doing fast operation. - real(R_P), intent(in) :: Dt !< Time steps. - real(R_P), intent(in) :: t(:) !< Times. - logical, intent(in), optional :: autoupdate !< Perform cyclic autoupdate of previous steps. - logical :: autoupdate_ !< Perform cyclic autoupdate of previous steps, - !< local variable. - integer(I_P) :: k, kk !< Stages counters. - integer(I_P) :: s !< Steps counter. - - autoupdate_ = .true. ; if (present(autoupdate)) autoupdate_ = autoupdate - ! computing stages - stage(1) = U - do k=2, self%stages - call stage(k)%multiply_fast(lhs=U, rhs=0._R_P) - do s=1, self%steps - if (self%D(k, s) /= 0._R_P) then - call buffer%multiply_fast(lhs=previous(s), rhs=self%D(k, s)) - call stage(k)%add_fast(lhs=stage(k), rhs=buffer) - endif - enddo - do s=1, self%steps - 1 - if (self%Ahat(k, s) /= 0._R_P) then - buffer = previous(s) - call buffer%t_fast(t=t(s)) - call buffer%multiply_fast(lhs=buffer, rhs=Dt * self%Ahat(k, s)) - call stage(k)%add_fast(lhs=stage(k), rhs=buffer) - endif - enddo - do kk=1, k - 1 - if (self%A(k, kk) /= 0._R_P) then - buffer = stage(kk) - call buffer%t_fast(t=t(self%steps)) - call buffer%multiply_fast(lhs=buffer, rhs=Dt * self%A(k, kk)) - call stage(k)%add_fast(lhs=stage(k), rhs=buffer) - endif - enddo - enddo - ! computing new time step - U = U * 0._R_P - do s=1, self%steps - if (self%T(s) /= 0._R_P) then - call buffer%multiply_fast(lhs=previous(s), rhs=self%T(s)) - call U%add_fast(lhs=U, rhs=buffer) - endif - enddo - do s=1, self%steps - 1 - if (self%Bhat(s) /= 0._R_P) then - buffer = previous(s) - call buffer%t_fast(t=t(s)) - call buffer%multiply_fast(lhs=buffer, rhs=Dt * self%Bhat(s)) - call U%add_fast(lhs=U, rhs=buffer) - endif - enddo - do k=1, self%stages - if (self%B(k) /= 0._R_P) U = U + (stage(k)%t(t=t(self%steps)) * (Dt * self%B(k))) - if (self%B(k) /= 0._R_P) then - buffer = stage(k) - call buffer%t_fast(t=t(self%steps)) - call buffer%multiply_fast(lhs=buffer, rhs=Dt * self%B(k)) - call U%add_fast(lhs=U, rhs=buffer) - endif - enddo - if (autoupdate_) call self%update_previous(U=U, previous=previous) - endsubroutine integrate_fast - - subroutine update_previous(self, U, previous) - !< Cyclic update previous time steps. - class(integrator_ms_runge_kutta_ssp), intent(in) :: self !< Integrator. - class(integrand_object), intent(in) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand field. - integer(I_P) :: s !< Steps counter. - - do s=1, self%steps - 1 - previous(s) = previous(s + 1) - enddo - previous(self%steps) = U - endsubroutine update_previous endmodule foodie_integrator_ms_runge_kutta_ssp diff --git a/src/lib/foodie_integrator_multistage_multistep_object.f90 b/src/lib/foodie_integrator_multistage_multistep_object.f90 new file mode 100644 index 00000000..e73d7bcd --- /dev/null +++ b/src/lib/foodie_integrator_multistage_multistep_object.f90 @@ -0,0 +1,213 @@ +!< Define the abstract type [[integrator_multistage_multistep_object]] of FOODIE ODE integrators. + +module foodie_integrator_multistage_multistep_object +!< Define the abstract type [[integrator_multistage_multistep_object]] of FOODIE ODE integrators. + +use, intrinsic :: iso_fortran_env, only : stderr=>error_unit +use foodie_integrand_object, only : integrand_object +use foodie_integrator_object, only : integrator_object +use penf, only : I_P, R_P + +implicit none +private +public :: integrator_multistage_multistep_object + +type, extends(integrator_object), abstract :: integrator_multistage_multistep_object + !< Abstract type of FOODIE ODE integrators of the multistage/multistep family. + integer(I_P) :: registers_stages !< Number of registers used for stages. + integer(I_P) :: registers_steps !< Number of registers used for steps. + integer(I_P) :: stages !< Number of stages. + integer(I_P) :: steps !< Number of time steps. + logical :: autoupdate !< Perform cyclic autoupdate of previous time steps buffers. + integer(I_P) :: iterations !< Implicit iterations. + real(R_P), allocatable :: Dt(:) !< Previous time steps. + real(R_P), allocatable :: t(:) !< Previous times. + class(integrand_object), allocatable :: previous(:) !< Previous steps. + class(integrand_object), allocatable :: stage(:) !< Stages. + class(integrand_object), allocatable :: buffer !< Buffer used for fast integration. + contains + ! deferred methods + procedure(integrate_interface), pass(self), deferred :: integrate !< Integrate integrand field. + procedure(integrate_fast_interface), pass(self), deferred :: integrate_fast !< Integrate integrand field, fast mode. + ! implemented deferred methods of parent + procedure, pass(self) :: is_multistage !< Return .true. for multistage integrator. + procedure, pass(self) :: is_multistep !< Return .true. for multistep integrator. + procedure, pass(self) :: stages_number !< Return number of stages used. + procedure, pass(self) :: steps_number !< Return number of steps used. + ! public methods + procedure, pass(self) :: allocate_integrand_members !< Allocate integrand members. + procedure, pass(lhs) :: assign_multistage_multistep !< Assign members of [[integrator_multistage_multistep_object]]. + procedure, pass(self) :: destroy_multistage_multistep !< Destroy the integrator. + procedure, nopass :: update_previous !< Cyclic update previous time steps. +endtype integrator_multistage_multistep_object + +abstract interface + !< Abstract interfaces of deferred methods of [[integrator_multistage_multistep_object]]. + subroutine integrate_interface(self, U, Dt, t) + !< Integrate integrand field. + import :: integrand_object, integrator_multistage_multistep_object, R_P + class(integrator_multistage_multistep_object), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Integrand. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + endsubroutine integrate_interface + + subroutine integrate_fast_interface(self, U, Dt, t) + !< Integrate integrand field, fast mode. + import :: integrand_object, integrator_multistage_multistep_object, R_P + class(integrator_multistage_multistep_object), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + endsubroutine integrate_fast_interface +endinterface + +contains + ! deferred methods + elemental function is_multistage(self) + !< Return .true. for multistage integrator. + class(integrator_multistage_multistep_object), intent(in) :: self !< Integrator. + logical :: is_multistage !< Inquire result. + + is_multistage = .true. + endfunction is_multistage + + elemental function is_multistep(self) + !< Return .true. for multistage integrator. + class(integrator_multistage_multistep_object), intent(in) :: self !< Integrator. + logical :: is_multistep !< Inquire result. + + is_multistep = .true. + endfunction is_multistep + + elemental function stages_number(self) + !< Return number of stages used. + class(integrator_multistage_multistep_object), intent(in) :: self !< Integrator. + integer(I_P) :: stages_number !< Number of stages used. + + stages_number = self%stages + endfunction stages_number + + elemental function steps_number(self) + !< Return number of steps used. + class(integrator_multistage_multistep_object), intent(in) :: self !< Integrator. + integer(I_P) :: steps_number !< Number of steps used. + + steps_number = self%steps + endfunction steps_number + + ! public methods + pure subroutine allocate_integrand_members(self, U) + !< Allocate members of interpolator being of [[integrand_object]] class. + !< + !< @note It is assumed that the integrator has been properly initialized before calling this method. + class(integrator_multistage_multistep_object), intent(inout) :: self !< Integrator. + class(integrand_object), intent(in) :: U !< Integrand. + integer(I_P) :: s !< Counter. + + if (self%is_multistage() .and. self%registers_stages > 0) then + if (allocated(self%stage)) deallocate(self%stage) + allocate(self%stage(1:self%registers_stages), mold=U) + do s=1, self%registers_stages + self%stage(s) = U + enddo + endif + if (self%is_multistep() .and. self%registers_steps > 0) then + if (allocated(self%Dt)) deallocate(self%Dt) + allocate(self%Dt(1:self%registers_steps)) ; self%Dt = 0._R_P + if (allocated(self%t)) deallocate(self%t) + allocate(self%t(1:self%registers_steps)) ; self%t = 0._R_P + if (allocated(self%previous)) deallocate(self%previous) + allocate(self%previous(1:self%registers_steps), mold=U) + do s=1, self%registers_steps + self%previous(s) = U + enddo + endif + if (self%has_fast_mode()) then + if (allocated(self%buffer)) deallocate(self%buffer) + allocate(self%buffer, mold=U) + self%buffer = U + endif + endsubroutine allocate_integrand_members + + pure subroutine assign_multistage_multistep(lhs, rhs) + !< Assign members of [[integrator_multistage_multistep_object]] and parents. + class(integrator_multistage_multistep_object), intent(inout) :: lhs !< Left hand side. + class(integrator_object), intent(in) :: rhs !< Right hand side. + integer(I_P) :: s !< Counter. + + call lhs%assign_abstract(rhs=rhs) + select type(rhs) + class is (integrator_multistage_multistep_object) + lhs%registers_stages = rhs%registers_stages + lhs%registers_steps = rhs%registers_steps + lhs%stages = rhs%stages + lhs%steps = rhs%steps + lhs%autoupdate = rhs%autoupdate + lhs%iterations = rhs%iterations + if (allocated(lhs%Dt)) deallocate(lhs%Dt) + if (allocated(rhs%Dt)) lhs%Dt = rhs%Dt + if (allocated(lhs%t)) deallocate(lhs%t) + if (allocated(rhs%t)) lhs%t = rhs%t + if (allocated(lhs%previous)) deallocate(lhs%previous) + if (allocated(rhs%previous)) then + allocate(lhs%previous(1:lhs%registers_steps), mold=rhs%previous) + do s=1, lhs%registers_steps + lhs%previous(s) = rhs%previous(s) + enddo + endif + if (allocated(lhs%stage)) deallocate(lhs%stage) + if (allocated(rhs%stage)) then + allocate(lhs%stage(1:lhs%registers_stages), mold=rhs%stage) + do s=1, lhs%registers_stages + lhs%stage(s) = rhs%stage(s) + enddo + endif + if (allocated(lhs%buffer)) deallocate(lhs%buffer) + if (allocated(rhs%buffer)) then + allocate(lhs%buffer, mold=rhs%buffer) + lhs%buffer = rhs%buffer + endif + endselect + endsubroutine assign_multistage_multistep + + elemental subroutine destroy_multistage_multistep(self) + !< Destroy the integrator. + class(integrator_multistage_multistep_object), intent(inout) :: self !< Integrator. + + call self%destroy_abstract + self%registers_stages = 0 + self%registers_steps = 0 + self%stages = 0 + self%steps = -1 + self%autoupdate = .false. + self%iterations = 0 + if (allocated(self%Dt)) deallocate(self%Dt) + if (allocated(self%t)) deallocate(self%t) + if (allocated(self%previous)) deallocate(self%previous) + if (allocated(self%stage)) deallocate(self%stage) + if (allocated(self%buffer)) deallocate(self%buffer) + endsubroutine destroy_multistage_multistep + + subroutine update_previous(U, previous, Dt, t, previous_Dt, previous_t) + !< Cyclic update previous time steps. + class(integrand_object), intent(in) :: U !< Field to be integrated. + class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand. + real(R_P), intent(in), optional :: Dt !< Time step. + real(R_P), intent(in), optional :: t !< Time. + real(R_P), intent(inout), optional :: previous_Dt(1:) !< Time step. + real(R_P), intent(inout), optional :: previous_t(1:) !< Time. + integer(I_P) :: last_step !< Last step. + integer(I_P) :: s !< Steps counter. + + last_step = size(previous, dim=1) + do s=1, last_step - 1 + previous(s) = previous(s + 1) + if (present(previous_Dt)) previous_Dt(s) = previous_Dt(s + 1) + if (present(previous_t)) previous_t(s) = previous_t(s + 1) + enddo + previous(last_step) = U + if (present(previous_Dt)) previous_Dt(last_step) = Dt + if (present(previous_t)) previous_t(last_step) = t + Dt + endsubroutine update_previous +endmodule foodie_integrator_multistage_multistep_object diff --git a/src/lib/foodie_integrator_multistage_object.f90 b/src/lib/foodie_integrator_multistage_object.f90 new file mode 100644 index 00000000..f633f8ad --- /dev/null +++ b/src/lib/foodie_integrator_multistage_object.f90 @@ -0,0 +1,152 @@ +!< Define the abstract type [[integrator_multistage_object]] of FOODIE ODE integrators. + +module foodie_integrator_multistage_object +!< Define the abstract type [[integrator_multistage_object]] of FOODIE ODE integrators. + +use, intrinsic :: iso_fortran_env, only : stderr=>error_unit +use foodie_integrand_object, only : integrand_object +use foodie_integrator_object, only : integrator_object +use penf, only : I_P, R_P + +implicit none +private +public :: integrator_multistage_object + +type, extends(integrator_object), abstract :: integrator_multistage_object + !< Abstract type of FOODIE ODE integrators of the multistage family. + integer(I_P) :: registers !< Number of registers used for stages. + integer(I_P) :: stages !< Number of stages. + class(integrand_object), allocatable :: stage(:) !< Stages. + class(integrand_object), allocatable :: buffer !< Buffer used for fast integration. + contains + ! deferred methods + procedure(integrate_interface), pass(self), deferred :: integrate !< Integrate integrand field. + procedure(integrate_fast_interface), pass(self), deferred :: integrate_fast !< Integrate integrand field, fast mode. + ! implemented deferred methods of parent + procedure, pass(self) :: is_multistage !< Return .true. for multistage integrator. + procedure, pass(self) :: is_multistep !< Return .true. for multistep integrator. + procedure, pass(self) :: stages_number !< Return number of stages used. + procedure, pass(self) :: steps_number !< Return number of steps used. + ! public methods + procedure, pass(self) :: allocate_integrand_members !< Allocate integrand members. + procedure, pass(lhs) :: assign_multistage !< Assign members of [[integrator_multistage_object]] and parents. + procedure, pass(self) :: destroy_multistage !< Destroy the integrator. +endtype integrator_multistage_object + +abstract interface + !< Abstract interfaces of deferred methods of [[integrator_multistage_object]]. + subroutine integrate_interface(self, U, Dt, t, new_Dt) + !< Integrate integrand field. + import :: integrand_object, integrator_multistage_object, R_P + class(integrator_multistage_object), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Integrand. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + real(R_P), intent(out), optional :: new_Dt !< New adapted time step. + endsubroutine integrate_interface + + subroutine integrate_fast_interface(self, U, Dt, t, new_Dt) + !< Integrate integrand field, fast mode. + import :: integrand_object, integrator_multistage_object, R_P + class(integrator_multistage_object), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + real(R_P), intent(out), optional :: new_Dt !< New adapted time step. + endsubroutine integrate_fast_interface +endinterface + +contains + ! deferred methods + elemental function is_multistage(self) + !< Return .true. for multistage integrator. + class(integrator_multistage_object), intent(in) :: self !< Integrator. + logical :: is_multistage !< Inquire result. + + is_multistage = .true. + endfunction is_multistage + + elemental function is_multistep(self) + !< Return .true. for multistage integrator. + class(integrator_multistage_object), intent(in) :: self !< Integrator. + logical :: is_multistep !< Inquire result. + + is_multistep = .false. + endfunction is_multistep + + elemental function stages_number(self) + !< Return number of stages used. + class(integrator_multistage_object), intent(in) :: self !< Integrator. + integer(I_P) :: stages_number !< Number of stages used. + + stages_number = self%stages + endfunction stages_number + + elemental function steps_number(self) + !< Return number of steps used. + class(integrator_multistage_object), intent(in) :: self !< Integrator. + integer(I_P) :: steps_number !< Number of steps used. + + steps_number = 0 + endfunction steps_number + + ! public methods + pure subroutine allocate_integrand_members(self, U) + !< Allocate members of interpolator being of [[integrand_object]] class. + !< + !< @note It is assumed that the integrator has been properly initialized before calling this method. + class(integrator_multistage_object), intent(inout) :: self !< Integrator. + class(integrand_object), intent(in) :: U !< Integrand. + integer(I_P) :: s !< Counter. + + if (self%is_multistage() .and. self%registers > 0) then + if (allocated(self%stage)) deallocate(self%stage) + allocate(self%stage(1:self%registers), mold=U) + do s=1, self%registers + self%stage(s) = U + enddo + endif + if (self%has_fast_mode()) then + if (allocated(self%buffer)) deallocate(self%buffer) + allocate(self%buffer, mold=U) + self%buffer = U + endif + endsubroutine allocate_integrand_members + + pure subroutine assign_multistage(lhs, rhs) + !< Assign members of [[integrator_multistage_object]] and parents. + class(integrator_multistage_object), intent(inout) :: lhs !< Left hand side. + class(integrator_object), intent(in) :: rhs !< Right hand side. + integer(I_P) :: s !< Counter. + + call lhs%assign_abstract(rhs=rhs) + select type(rhs) + class is (integrator_multistage_object) + lhs%registers = rhs%registers + lhs%stages = rhs%stages + if (allocated(lhs%stage)) deallocate(lhs%stage) + if (allocated(rhs%stage)) then + allocate(lhs%stage(1:lhs%registers), mold=rhs%stage) + do s=1, lhs%registers + lhs%stage(s) = rhs%stage(s) + enddo + endif + if (allocated(lhs%buffer)) deallocate(lhs%buffer) + if (allocated(rhs%buffer)) then + allocate(lhs%buffer, mold=rhs%buffer) + lhs%buffer = rhs%buffer + endif + endselect + endsubroutine assign_multistage + + elemental subroutine destroy_multistage(self) + !< Destroy the integrator. + class(integrator_multistage_object), intent(inout) :: self !< Integrator. + + call self%destroy_abstract + self%registers = 0 + self%stages = 0 + if (allocated(self%stage)) deallocate(self%stage) + if (allocated(self%buffer)) deallocate(self%buffer) + endsubroutine destroy_multistage +endmodule foodie_integrator_multistage_object diff --git a/src/lib/foodie_integrator_multistep_object.f90 b/src/lib/foodie_integrator_multistep_object.f90 new file mode 100644 index 00000000..87dfca5e --- /dev/null +++ b/src/lib/foodie_integrator_multistep_object.f90 @@ -0,0 +1,191 @@ +!< Define the abstract type [[integrator_multistep_object]] of FOODIE ODE integrators. + +module foodie_integrator_multistep_object +!< Define the abstract type [[integrator_multistep_object]] of FOODIE ODE integrators. + +use, intrinsic :: iso_fortran_env, only : stderr=>error_unit +use foodie_integrand_object, only : integrand_object +use foodie_integrator_object, only : integrator_object +use penf, only : I_P, R_P + +implicit none +private +public :: integrator_multistep_object + +type, extends(integrator_object), abstract :: integrator_multistep_object + !< Abstract type of FOODIE ODE integrators of the multistep-implicit family. + integer(I_P) :: registers !< Number of registers used for steps. + integer(I_P) :: steps !< Number of time steps. + logical :: autoupdate !< Perform cyclic autoupdate of previous time steps buffers. + integer(I_P) :: iterations !< Implicit iterations. + real(R_P), allocatable :: Dt(:) !< Previous time steps. + real(R_P), allocatable :: t(:) !< Previous times. + class(integrand_object), allocatable :: previous(:) !< Previous steps. + class(integrand_object), allocatable :: buffer !< Buffer used for fast integration. + contains + ! deferred methods + procedure(integrate_interface), pass(self), deferred :: integrate !< Integrate integrand field. + procedure(integrate_fast_interface), pass(self), deferred :: integrate_fast !< Integrate integrand field, fast mode. + ! implemented deferred methods of parent + procedure, pass(self) :: is_multistage !< Return .true. for multistage integrator. + procedure, pass(self) :: is_multistep !< Return .true. for multistep integrator. + procedure, pass(self) :: stages_number !< Return number of stages used. + procedure, pass(self) :: steps_number !< Return number of steps used. + ! public methods + procedure, pass(self) :: allocate_integrand_members !< Allocate integrand members. + procedure, pass(lhs) :: assign_multistep !< Assign members of [[integrator_multistep_object]] and parents. + procedure, pass(self) :: destroy_multistep !< Destroy the integrator. + procedure, nopass :: update_previous !< Cyclic update previous time steps. +endtype integrator_multistep_object + +abstract interface + !< Abstract interfaces of deferred methods of [[integrator_multistep_object]]. + subroutine integrate_interface(self, U, Dt, t) + !< Integrate integrand field. + import :: integrand_object, integrator_multistep_object, R_P + class(integrator_multistep_object), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Integrand. + real(R_P), intent(in) :: Dt !< Time steps. + real(R_P), intent(in) :: t !< Times. + endsubroutine integrate_interface + + subroutine integrate_fast_interface(self, U, Dt, t) + !< Integrate integrand field, fast mode. + import :: integrand_object, integrator_multistep_object, R_P + class(integrator_multistep_object), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time steps. + real(R_P), intent(in) :: t !< Times. + endsubroutine integrate_fast_interface +endinterface + +contains + ! deferred methods + elemental function is_multistage(self) + !< Return .true. for multistage integrator. + class(integrator_multistep_object), intent(in) :: self !< Integrator. + logical :: is_multistage !< Inquire result. + + is_multistage = .false. + endfunction is_multistage + + elemental function is_multistep(self) + !< Return .true. for multistage integrator. + class(integrator_multistep_object), intent(in) :: self !< Integrator. + logical :: is_multistep !< Inquire result. + + is_multistep = .true. + endfunction is_multistep + + elemental function stages_number(self) + !< Return number of stages used. + class(integrator_multistep_object), intent(in) :: self !< Integrator. + integer(I_P) :: stages_number !< Number of stages used. + + stages_number = 0 + endfunction stages_number + + elemental function steps_number(self) + !< Return number of steps used. + class(integrator_multistep_object), intent(in) :: self !< Integrator. + integer(I_P) :: steps_number !< Number of steps used. + + steps_number = self%steps + endfunction steps_number + + ! public methods + pure subroutine allocate_integrand_members(self, U) + !< Allocate members of interpolator being of [[integrand_object]] class. + !< + !< @note It is assumed that the integrator has been properly initialized before calling this method. + class(integrator_multistep_object), intent(inout) :: self !< Integrator. + class(integrand_object), intent(in) :: U !< Integrand. + integer(I_P) :: s !< Counter. + + if (self%is_multistep() .and. self%registers > 0) then + if (allocated(self%Dt)) deallocate(self%Dt) + allocate(self%Dt(1:self%registers)) ; self%Dt = 0._R_P + if (allocated(self%t)) deallocate(self%t) + allocate(self%t(1:self%registers)) ; self%t = 0._R_P + if (allocated(self%previous)) deallocate(self%previous) + allocate(self%previous(1:self%registers), mold=U) + do s=1, self%registers + self%previous(s) = U + enddo + endif + if (self%has_fast_mode()) then + if (allocated(self%buffer)) deallocate(self%buffer) + allocate(self%buffer, mold=U) + self%buffer = U + endif + endsubroutine allocate_integrand_members + + pure subroutine assign_multistep(lhs, rhs) + !< Assign members of [[integrator_multistep_object]] and parents. + class(integrator_multistep_object), intent(inout) :: lhs !< Left hand side. + class(integrator_object), intent(in) :: rhs !< Right hand side. + integer(I_P) :: s !< Counter. + + call lhs%assign_abstract(rhs=rhs) + select type(rhs) + class is (integrator_multistep_object) + lhs%registers = rhs%registers + lhs%steps = rhs%steps + lhs%autoupdate = rhs%autoupdate + lhs%iterations = rhs%iterations + if (allocated(lhs%Dt)) deallocate(lhs%Dt) + if (allocated(rhs%Dt)) lhs%Dt = rhs%Dt + if (allocated(lhs%t)) deallocate(lhs%t) + if (allocated(rhs%t)) lhs%t = rhs%t + if (allocated(lhs%previous)) deallocate(lhs%previous) + if (allocated(rhs%previous)) then + allocate(lhs%previous(1:lhs%registers), mold=rhs%previous) + do s=1, lhs%registers + lhs%previous(s) = rhs%previous(s) + enddo + endif + if (allocated(lhs%buffer)) deallocate(lhs%buffer) + if (allocated(rhs%buffer)) then + allocate(lhs%buffer, mold=rhs%buffer) + lhs%buffer = rhs%buffer + endif + endselect + endsubroutine assign_multistep + + elemental subroutine destroy_multistep(self) + !< Destroy the integrator. + class(integrator_multistep_object), intent(inout) :: self !< Integrator. + + call self%destroy_abstract + self%registers = 0 + self%steps = -1 + self%autoupdate = .false. + self%iterations = 0 + if (allocated(self%Dt)) deallocate(self%Dt) + if (allocated(self%t)) deallocate(self%t) + if (allocated(self%previous)) deallocate(self%previous) + if (allocated(self%buffer)) deallocate(self%buffer) + endsubroutine destroy_multistep + + subroutine update_previous(U, previous, Dt, t, previous_Dt, previous_t) + !< Cyclic update previous time steps. + class(integrand_object), intent(in) :: U !< Field to be integrated. + class(integrand_object), intent(inout) :: previous(1:) !< Previous time steps solutions of integrand. + real(R_P), intent(in), optional :: Dt !< Time step. + real(R_P), intent(in), optional :: t !< Time. + real(R_P), intent(inout), optional :: previous_Dt(1:) !< Time step. + real(R_P), intent(inout), optional :: previous_t(1:) !< Time. + integer(I_P) :: last_step !< Last step. + integer(I_P) :: s !< Steps counter. + + last_step = size(previous, dim=1) + do s=1, last_step - 1 + previous(s) = previous(s + 1) + if (present(previous_Dt)) previous_Dt(s) = previous_Dt(s + 1) + if (present(previous_t)) previous_t(s) = previous_t(s + 1) + enddo + previous(last_step) = U + if (present(previous_Dt)) previous_Dt(last_step) = Dt + if (present(previous_t)) previous_t(last_step) = t + Dt + endsubroutine update_previous +endmodule foodie_integrator_multistep_object diff --git a/src/lib/foodie_integrator_object.f90 b/src/lib/foodie_integrator_object.f90 index e34d44f9..d74d36d4 100644 --- a/src/lib/foodie_integrator_object.f90 +++ b/src/lib/foodie_integrator_object.f90 @@ -1,7 +1,7 @@ -!< Define the abstract type *integrator* of FOODIE ODE integrators. +!< Define the abstract type [[integrator_object]] of FOODIE ODE integrators. module foodie_integrator_object -!< Define the abstract type *integrator* of FOODIE ODE integrators. +!< Define the abstract type [[integrator_object]] of FOODIE ODE integrators. use, intrinsic :: iso_fortran_env, only : stderr=>error_unit use penf, only : I_P @@ -12,17 +12,18 @@ module foodie_integrator_object type, abstract :: integrator_object !< Abstract type of FOODIE ODE integrators. + character(len=:), allocatable :: description_ !< Informative description of the integrator. integer(I_P) :: error=0 !< Error status code. character(len=:), allocatable :: error_message !< Error message, hopefully meaningful. contains ! public methods procedure, pass(lhs) :: assign_abstract !< Assign ony members of abstract [[integrator_object]] type. procedure, pass(self) :: check_error !< Check for error occurrencies. + procedure, pass(self) :: description !< Return informative integrator description. procedure, pass(self) :: destroy_abstract !< Destroy only members of abstract [[integrator_object]] type. procedure, pass(self) :: trigger_error !< Trigger an error. ! deferred methods procedure(class_name_interface), pass(self), deferred :: class_name !< Return the class name of schemes. - procedure(description_interface), pass(self), deferred :: description !< Return pretty-printed obj. description. procedure(has_fast_mode_interface), pass(self), deferred :: has_fast_mode !< Return .true. if the integrator class !< has *fast mode* integrate. procedure(assignment_interface), pass(lhs), deferred :: integr_assign_integr !< Operator `=`. @@ -105,6 +106,7 @@ pure subroutine assign_abstract(lhs, rhs) class(integrator_object), intent(inout) :: lhs !< Left hand side. class(integrator_object), intent(in) :: rhs !< Right hand side. + if (allocated(rhs%description_ )) lhs%description_ = rhs%description_ lhs%error = rhs%error if (allocated(rhs%error_message)) lhs%error_message = rhs%error_message endsubroutine assign_abstract @@ -129,10 +131,26 @@ subroutine check_error(self, is_severe) endif endsubroutine check_error + pure function description(self, prefix) result(desc) + !< Return informative integrator description. + class(integrator_object), intent(in) :: self !< Integrator. + character(*), intent(in), optional :: prefix !< Prefixing string. + character(len=:), allocatable :: desc !< Description. + character(len=:), allocatable :: prefix_ !< Prefixing string, local variable. + + prefix_ = '' ; if (present(prefix)) prefix_ = prefix + if (allocated(self%description_)) then + desc = prefix//self%description_ + else + desc = prefix//self%class_name() + endif + endfunction description + elemental subroutine destroy_abstract(self) !< Destroy only members of abstract [[integrator_object]] type. class(integrator_object), intent(inout) :: self !< Integrator. + if (allocated(self%description_)) deallocate(self%description_) self%error = 0 if (allocated(self%error_message)) deallocate(self%error_message) endsubroutine destroy_abstract diff --git a/src/lib/foodie_integrator_runge_kutta_embedded.f90 b/src/lib/foodie_integrator_runge_kutta_embedded.f90 index 46a00e61..7e1d84a7 100644 --- a/src/lib/foodie_integrator_runge_kutta_embedded.f90 +++ b/src/lib/foodie_integrator_runge_kutta_embedded.f90 @@ -268,6 +268,7 @@ module foodie_integrator_runge_kutta_emd use foodie_error_codes, only : ERROR_UNSUPPORTED_SCHEME use foodie_integrand_object, only : integrand_object +use foodie_integrator_multistage_object, only : integrator_multistage_object use foodie_integrator_object, only : integrator_object use penf, only : I_P, R_P @@ -282,42 +283,38 @@ module foodie_integrator_runge_kutta_emd trim(class_name_)//'_stages_17_order_10'] !< List of supported schemes. logical, parameter :: has_fast_mode_=.true. !< Flag to check if integrator provides *fast mode* integrate. -logical, parameter :: is_multistage_=.true. !< Flag to check if integrator is multistage. -logical, parameter :: is_multistep_=.false. !< Flag to check if integrator is multistep. -type, extends(integrator_object) :: integrator_runge_kutta_emd - !< FOODIE integrator: provide an explicit class of embedded Runge-Kutta schemes, from 2nd to 10th order accurate. - !< - !< @note The integrator must be created or initialized (initialize the RK coefficients) before used. - private - real(R_P) :: tolerance=0._R_P !< Tolerance on the local truncation error. - real(R_P) :: pp1_inv=0._R_P !< \(1/(p+1)\) where p is the accuracy order of the lower accurate scheme of the pair. - integer(I_P) :: stages=0 !< Number of stages. - real(R_P), allocatable :: alph(:,:) !< \(\alpha\) Butcher's coefficients. - real(R_P), allocatable :: beta(:,:) !< \(\beta\) Butcher's coefficients. - real(R_P), allocatable :: gamm(:) !< \(\gamma\) Butcher's coefficients. - contains - ! deferred methods - procedure, pass(self) :: class_name !< Return the class name of schemes. - procedure, pass(self) :: description !< Return pretty-printed object description. - procedure, pass(self) :: has_fast_mode !< Return .true. if the integrator class has *fast mode* integrate. - procedure, pass(lhs) :: integr_assign_integr !< Operator `=`. - procedure, pass(self) :: is_multistage !< Return .true. for multistage integrator. - procedure, pass(self) :: is_multistep !< Return .true. for multistep integrator. - procedure, pass(self) :: is_supported !< Return .true. if the integrator class support the given scheme. - procedure, pass(self) :: stages_number !< Return number of stages used. - procedure, pass(self) :: steps_number !< Return number of steps used. - procedure, pass(self) :: supported_schemes !< Return the list of supported schemes. - ! public methods - procedure, pass(self) :: destroy !< Destroy the integrator. - procedure, pass(self) :: initialize !< Initialize (create) the integrator. - procedure, pass(self) :: integrate !< Integrate integrand field. - procedure, pass(self) :: integrate_fast !< Integrate integrand field, fast mode. - ! private methods - procedure, pass(self), private :: new_Dt !< Compute new estimation of the time step Dt. +type, extends(integrator_multistage_object) :: integrator_runge_kutta_emd + !< FOODIE integrator: provide an explicit class of embedded Runge-Kutta schemes, from 2nd to 10th order accurate. + !< + !< @note The integrator must be created or initialized (initialize the RK coefficients) before used. + private + real(R_P) :: tolerance !< Tolerance on the local truncation error. + real(R_P) :: pp1_inv !< \(1/(p+1)\) where p is the accuracy order of the lower accurate scheme. + real(R_P), allocatable :: alph(:,:) !< \(\alpha\) Butcher's coefficients. + real(R_P), allocatable :: beta(:,:) !< \(\beta\) Butcher's coefficients. + real(R_P), allocatable :: gamm(:) !< \(\gamma\) Butcher's coefficients. + class(integrand_object), allocatable :: U1 !< First U evaluation. + class(integrand_object), allocatable :: U2 !< Second U evaluation. + contains + ! deferred methods + procedure, pass(self) :: class_name !< Return the class name of schemes. + procedure, pass(self) :: has_fast_mode !< Return .true. if the integrator class has *fast mode* integrate. + procedure, pass(lhs) :: integr_assign_integr !< Operator `=`. + procedure, pass(self) :: integrate !< Integrate integrand field. + procedure, pass(self) :: integrate_fast !< Integrate integrand field, fast mode. + procedure, pass(self) :: is_supported !< Return .true. if the integrator class support the given scheme. + procedure, pass(self) :: supported_schemes !< Return the list of supported schemes. + ! public methods + procedure, pass(self) :: destroy !< Destroy the integrator. + procedure, pass(self) :: initialize !< Initialize (create) the integrator. + ! overridden public methods + procedure, pass(self) :: allocate_integrand_members !< Allocate integrand members. + ! private methods + procedure, pass(self), private :: new_Dt !< Compute new estimation of the time step Dt. endtype integrator_runge_kutta_emd contains - ! deferred methods + ! deferred methods pure function class_name(self) !< Return the class name of schemes. class(integrator_runge_kutta_emd), intent(in) :: self !< Integrator. @@ -326,25 +323,6 @@ pure function class_name(self) class_name = trim(adjustl(class_name_)) endfunction class_name - pure function description(self, prefix) result(desc) - !< Return a pretty-formatted object description. - class(integrator_runge_kutta_emd), intent(in) :: self !< Integrator. - character(*), intent(in), optional :: prefix !< Prefixing string. - character(len=:), allocatable :: desc !< Description. - character(len=:), allocatable :: prefix_ !< Prefixing string, local variable. - character(len=1), parameter :: NL=new_line('a') !< New line character. - integer(I_P) :: s !< Counter. - - prefix_ = '' ; if (present(prefix)) prefix_ = prefix - desc = '' - desc = desc//prefix_//'Embedded Runge-Kutta multi-stage schemes class'//NL - desc = desc//prefix_//' Supported schemes:'//NL - do s=lbound(supported_schemes_, dim=1), ubound(supported_schemes_, dim=1) - 1 - desc = desc//prefix_//' + '//supported_schemes_(s)//NL - enddo - desc = desc//prefix_//' + '//supported_schemes_(ubound(supported_schemes_, dim=1)) - endfunction description - elemental function has_fast_mode(self) !< Return .true. if the integrator class has *fast mode* integrate. class(integrator_runge_kutta_emd), intent(in) :: self !< Integrator. @@ -370,21 +348,86 @@ pure subroutine integr_assign_integr(lhs, rhs) endselect endsubroutine integr_assign_integr - elemental function is_multistage(self) - !< Return .true. for multistage integrator. - class(integrator_runge_kutta_emd), intent(in) :: self !< Integrator. - logical :: is_multistage !< Inquire result. - - is_multistage = is_multistage_ - endfunction is_multistage - - elemental function is_multistep(self) - !< Return .true. for multistage integrator. - class(integrator_runge_kutta_emd), intent(in) :: self !< Integrator. - logical :: is_multistep !< Inquire result. + subroutine integrate(self, U, Dt, t, new_Dt) + !< Integrate field with explicit embedded Runge-Kutta scheme. + !< + !< The time steps is adaptively resized using the local truncation error estimation by means of the embedded pairs of RK schemes. + class(integrator_runge_kutta_emd), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + real(R_P), intent(out), optional :: new_Dt !< New adapted time step. + real(R_P) :: Dt_ !< Time step, local variable. + real(R_P) :: error !< Local truncation error estimation. + integer(I_P) :: s !< First stages counter. + integer(I_P) :: ss !< Second stages counter. + + Dt_ = Dt + do + ! compute stages + do s=1, self%stages + self%stage(s) = U + do ss=1, s - 1 + self%stage(s) = self%stage(s) + (self%stage(ss) * (Dt_ * self%alph(s, ss))) + enddo + self%stage(s) = self%stage(s)%t(t=t + self%gamm(s) * Dt_) + enddo + ! compute new time step + self%U1 = U + self%U2 = U + do s=1, self%stages + self%U1 = self%U1 + (self%stage(s) * (Dt_ * self%beta(s, 1))) + self%U2 = self%U2 + (self%stage(s) * (Dt_ * self%beta(s, 2))) + enddo + error = self%U2.lterror.self%U1 + if (error <= self%tolerance) exit + call self%new_Dt(error=error, Dt=Dt_) + enddo + U = self%U1 + if (present(new_Dt)) new_Dt = Dt_ + endsubroutine integrate - is_multistep = is_multistep_ - endfunction is_multistep + subroutine integrate_fast(self, U, Dt, t, new_Dt) + !< Integrate field with explicit embedded Runge-Kutta scheme, fast mode. + !< + !< The time steps is adaptively resized using the local truncation error estimation by means of the embedded pairs of RK schemes. + class(integrator_runge_kutta_emd), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + real(R_P), intent(out), optional :: new_Dt !< New adapted time step. + real(R_P) :: Dt_ !< Time step, local variable. + real(R_P) :: error !< Local truncation error estimation. + integer(I_P) :: s !< First stages counter. + integer(I_P) :: ss !< Second stages counter. + + Dt_ = Dt + do + ! compute stages + do s=1, self%stages + self%stage(s) = U + do ss=1, s - 1 + call self%buffer%multiply_fast(lhs=self%stage(ss), rhs=Dt_ * self%alph(s, ss)) + call self%stage(s)%add_fast(lhs=self%stage(s), rhs=self%buffer) + enddo + call self%stage(s)%t_fast(t=t + self%gamm(s) * Dt_) + enddo + ! compute new time step + self%U1 = U + self%U2 = U + do s=1, self%stages + call self%buffer%multiply_fast(lhs=self%stage(s), rhs=Dt_ * self%beta(s, 1)) + call self%U1%add_fast(lhs=self%U1, rhs=self%buffer) + call self%buffer%multiply_fast(lhs=self%stage(s), rhs=Dt_ * self%beta(s, 2)) + call self%U2%add_fast(lhs=self%U2, rhs=self%buffer) + enddo + error = self%U2.lterror.self%U1 + if (error <= self%tolerance) exit + call self%new_Dt(error=error, Dt=Dt_) + enddo + U = self%U1 + if (present(new_Dt)) new_Dt = Dt_ + endsubroutine integrate_fast elemental function is_supported(self, scheme) !< Return .true. if the integrator class support the given scheme. @@ -402,22 +445,6 @@ elemental function is_supported(self, scheme) enddo endfunction is_supported - elemental function stages_number(self) - !< Return number of stages used. - class(integrator_runge_kutta_emd), intent(in) :: self !< Integrator. - integer(I_P) :: stages_number !< Number of stages used. - - stages_number = self%stages - endfunction stages_number - - elemental function steps_number(self) - !< Return number of steps used. - class(integrator_runge_kutta_emd), intent(in) :: self !< Integrator. - integer(I_P) :: steps_number !< Number of steps used. - - steps_number = 0 - endfunction steps_number - pure function supported_schemes(self) result(schemes) !< Return the list of supported schemes. class(integrator_runge_kutta_emd), intent(in) :: self !< Integrator. @@ -432,29 +459,26 @@ elemental subroutine destroy(self) !< Destroy the integrator. class(integrator_runge_kutta_emd), intent(inout) :: self !< Integrator. - call self%destroy_abstract + call self%destroy_multistage self%tolerance = 0._R_P self%pp1_inv = 0._R_P - self%stages = 0 if (allocated(self%alph)) deallocate(self%alph) if (allocated(self%beta)) deallocate(self%beta) if (allocated(self%gamm)) deallocate(self%gamm) endsubroutine destroy - subroutine initialize(self, scheme, tolerance, stop_on_fail) + subroutine initialize(self, scheme, U, tolerance, stop_on_fail) !< Create the actual RK integrator: initialize the Butcher' table coefficients. class(integrator_runge_kutta_emd), intent(inout) :: self !< Integrator. character(*), intent(in) :: scheme !< Selected scheme. + class(integrand_object), intent(in), optional :: U !< Integrand molding prototype. real(R_P), intent(in), optional :: tolerance !< Tolerance on the local truncation error (default 0.01). logical, intent(in), optional :: stop_on_fail !< Stop execution if initialization fail. if (self%is_supported(scheme=scheme)) then call self%destroy - if (present(tolerance)) then - self%tolerance = tolerance - else - self%tolerance = 0.01_R_P - endif + self%description_ = trim(adjustl(scheme)) + self%tolerance = 0.01_R_P ; if (present(tolerance)) self%tolerance = tolerance select case(trim(adjustl(scheme))) case('runge_kutta_emd_stages_2_order_2') ! do not use, seems to not work! self%stages = 2 @@ -705,6 +729,8 @@ subroutine initialize(self, scheme, tolerance, stop_on_fail) self%gamm(16) = 0.1_R_P self%gamm(17) = 1._R_P endselect + self%registers = self%stages + if (present(U)) call self%allocate_integrand_members(U=U) else call self%trigger_error(error=ERROR_UNSUPPORTED_SCHEME, & error_message='"'//trim(adjustl(scheme))//'" unsupported scheme', & @@ -712,89 +738,34 @@ subroutine initialize(self, scheme, tolerance, stop_on_fail) endif endsubroutine initialize - subroutine integrate(self, U, stage, Dt, t) - !< Integrate field with explicit embedded Runge-Kutta scheme. - !< - !< The time steps is adaptively resized using the local truncation error estimation by means of the embedded pairs of RK schemes. - class(integrator_runge_kutta_emd), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: stage(1:) !< Runge-Kutta stages [1:stages]. - real(R_P), intent(inout) :: Dt !< Time step. - real(R_P), intent(in) :: t !< Time. - class(integrand_object), allocatable :: U1 !< First U evaluation. - class(integrand_object), allocatable :: U2 !< Second U evaluation. - real(R_P) :: error !< Local truncation error estimation. - integer(I_P) :: s !< First stages counter. - integer(I_P) :: ss !< Second stages counter. - - allocate(U1, mold=U) ; U1 = U - allocate(U2, mold=U) ; U2 = U - error = 1e6 - do while(error>self%tolerance) - ! compute stages - do s=1, self%stages - stage(s) = U - do ss=1, s - 1 - stage(s) = stage(s) + (stage(ss) * (Dt * self%alph(s, ss))) - enddo - stage(s) = stage(s)%t(t=t + self%gamm(s) * Dt) - enddo - ! compute new time step - U1 = U - U2 = U - do s=1, self%stages - U1 = U1 + (stage(s) * (Dt * self%beta(s, 1))) - U2 = U2 + (stage(s) * (Dt * self%beta(s, 2))) - enddo - error = U2.lterror.U1 - call self%new_Dt(error=error, Dt=Dt) - enddo - U = U1 - endsubroutine integrate - - subroutine integrate_fast(self, U, stage, buffer, Dt, t) - !< Integrate field with explicit embedded Runge-Kutta scheme, fast mode. - !< - !< The time steps is adaptively resized using the local truncation error estimation by means of the embedded pairs of RK schemes. - class(integrator_runge_kutta_emd), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: stage(1:) !< Runge-Kutta stages [1:stages]. - class(integrand_object), intent(inout) :: buffer !< Temporary buffer for doing fast operation. - real(R_P), intent(inout) :: Dt !< Time step. - real(R_P), intent(in) :: t !< Time. - class(integrand_object), allocatable :: U1 !< First U evaluation. - class(integrand_object), allocatable :: U2 !< Second U evaluation. - real(R_P) :: error !< Local truncation error estimation. - integer(I_P) :: s !< First stages counter. - integer(I_P) :: ss !< Second stages counter. - - allocate(U1, mold=U) ; U1 = U - allocate(U2, mold=U) ; U2 = U - error = 1e6 - do while(error>self%tolerance) - ! compute stages - do s=1, self%stages - stage(s) = U - do ss=1, s - 1 - call buffer%multiply_fast(lhs=stage(ss), rhs=Dt * self%alph(s, ss)) - call stage(s)%add_fast(lhs=stage(s), rhs=buffer) + ! overridden public methods + pure subroutine allocate_integrand_members(self, U) + !< Allocate members of interpolator being of [[integrand_object]] class. + !< + !< @note It is assumed that the integrator has been properly initialized before calling this method. + class(integrator_runge_kutta_emd), intent(inout) :: self !< Integrator. + class(integrand_object), intent(in) :: U !< Integrand. + integer(I_P) :: s !< Counter. + + if (self%is_multistage() .and. self%registers > 0) then + if (allocated(self%stage)) deallocate(self%stage) + allocate(self%stage(1:self%registers), mold=U) + do s=1, self%registers + self%stage(s) = U enddo - call stage(s)%t_fast(t=t + self%gamm(s) * Dt) - enddo - ! compute new time step - U1 = U - U2 = U - do s=1, self%stages - call buffer%multiply_fast(lhs=stage(s), rhs=Dt * self%beta(s, 1)) - call U1%add_fast(lhs=U1, rhs=buffer) - call buffer%multiply_fast(lhs=stage(s), rhs=Dt * self%beta(s, 2)) - call U2%add_fast(lhs=U2, rhs=buffer) - enddo - error = U2.lterror.U1 - call self%new_Dt(error=error, Dt=Dt) - enddo - U = U1 - endsubroutine integrate_fast + endif + if (self%has_fast_mode()) then + if (allocated(self%buffer)) deallocate(self%buffer) + allocate(self%buffer, mold=U) + self%buffer = U + endif + if (allocated(self%U1)) deallocate(self%U1) + allocate(self%U1, mold=U) + self%U1 = U + if (allocated(self%U2)) deallocate(self%U2) + allocate(self%U2, mold=U) + self%U2 = U + endsubroutine allocate_integrand_members ! private methods elemental subroutine new_Dt(self, error, Dt) diff --git a/src/lib/foodie_integrator_runge_kutta_low_storage.f90 b/src/lib/foodie_integrator_runge_kutta_low_storage.f90 index a48580e0..4803239a 100644 --- a/src/lib/foodie_integrator_runge_kutta_low_storage.f90 +++ b/src/lib/foodie_integrator_runge_kutta_low_storage.f90 @@ -149,6 +149,7 @@ module foodie_integrator_runge_kutta_low_storage use foodie_error_codes, only : ERROR_UNSUPPORTED_SCHEME use foodie_integrand_object, only : integrand_object +use foodie_integrator_multistage_object, only : integrator_multistage_object use foodie_integrator_object, only : integrator_object use penf, only : I_P, I8P, R_P @@ -168,40 +169,32 @@ module foodie_integrator_runge_kutta_low_storage integer(I_P), parameter :: registers=2 !< Registers used (2N schemes). logical, parameter :: has_fast_mode_=.true. !< Flag to check if integrator provides *fast mode* integrate. -logical, parameter :: is_multistage_=.true. !< Flag to check if integrator is multistage. -logical, parameter :: is_multistep_=.false. !< Flag to check if integrator is multistep. -type, extends(integrator_object) :: integrator_runge_kutta_ls +type, extends(integrator_multistage_object) :: integrator_runge_kutta_ls !< FOODIE integrator: provide an explicit class of low storage Runge-Kutta schemes, from 1st to 4th order accurate. !< !< @note The integrator must be created or initialized (initialize the RK coefficients) before used. private - integer(I_P) :: stages=0 !< Number of stages. - real(R_P), allocatable :: A(:) !< Low storage *A* coefficients. - real(R_P), allocatable :: B(:) !< Low storage *B* coefficients. - real(R_P), allocatable :: C(:) !< Low storage *C* coefficients. + real(R_P), allocatable :: A(:) !< Low storage *A* coefficients. + real(R_P), allocatable :: B(:) !< Low storage *B* coefficients. + real(R_P), allocatable :: C(:) !< Low storage *C* coefficients. contains ! deferred methods procedure, pass(self) :: class_name !< Return the class name of schemes. - procedure, pass(self) :: description !< Return pretty-printed object description. procedure, pass(self) :: has_fast_mode !< Return .true. if the integrator class has *fast mode* integrate. procedure, pass(lhs) :: integr_assign_integr !< Operator `=`. - procedure, pass(self) :: is_multistage !< Return .true. for multistage integrator. - procedure, pass(self) :: is_multistep !< Return .true. for multistep integrator. + procedure, pass(self) :: integrate !< Integrate integrand field. + procedure, pass(self) :: integrate_fast !< Integrate integrand field, fast mode. procedure, pass(self) :: is_supported !< Return .true. if the integrator class support the given scheme. - procedure, pass(self) :: stages_number !< Return number of stages used. - procedure, pass(self) :: steps_number !< Return number of steps used. procedure, pass(self) :: supported_schemes !< Return the list of supported schemes. ! public methods procedure, pass(self) :: destroy !< Destroy the integrator. procedure, pass(self) :: initialize !< Initialize (create) the integrator. - procedure, pass(self) :: integrate !< Integrate integrand field. - procedure, pass(self) :: integrate_fast !< Integrate integrand field, fast mode. procedure, nopass :: registers_number !< Return the number of registers used. endtype integrator_runge_kutta_ls contains - ! deferred methods + ! deferred methods pure function class_name(self) !< Return the class name of schemes. class(integrator_runge_kutta_ls), intent(in) :: self !< Integrator. @@ -210,25 +203,6 @@ pure function class_name(self) class_name = trim(adjustl(class_name_)) endfunction class_name - pure function description(self, prefix) result(desc) - !< Return a pretty-formatted object description. - class(integrator_runge_kutta_ls), intent(in) :: self !< Integrator. - character(*), intent(in), optional :: prefix !< Prefixing string. - character(len=:), allocatable :: desc !< Description. - character(len=:), allocatable :: prefix_ !< Prefixing string, local variable. - character(len=1), parameter :: NL=new_line('a') !< New line character. - integer(I_P) :: s !< Counter. - - prefix_ = '' ; if (present(prefix)) prefix_ = prefix - desc = '' - desc = desc//prefix_//'Low storage (2-registers) Runge-Kutta multi-stage schemes class'//NL - desc = desc//prefix_//' Supported schemes:'//NL - do s=lbound(supported_schemes_, dim=1), ubound(supported_schemes_, dim=1) - 1 - desc = desc//prefix_//' + '//supported_schemes_(s)//NL - enddo - desc = desc//prefix_//' + '//supported_schemes_(ubound(supported_schemes_, dim=1)) - endfunction description - elemental function has_fast_mode(self) !< Return .true. if the integrator class has *fast mode* integrate. class(integrator_runge_kutta_ls), intent(in) :: self !< Integrator. @@ -252,21 +226,50 @@ pure subroutine integr_assign_integr(lhs, rhs) endselect endsubroutine integr_assign_integr - elemental function is_multistage(self) - !< Return .true. for multistage integrator. - class(integrator_runge_kutta_ls), intent(in) :: self !< Integrator. - logical :: is_multistage !< Inquire result. + subroutine integrate(self, U, Dt, t, new_Dt) + !< Integrate field with explicit low storage Runge-Kutta scheme. + class(integrator_runge_kutta_ls), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + real(R_P), intent(out), optional :: new_Dt !< New adapted time step. + integer(I_P) :: s !< First stages counter. - is_multistage = is_multistage_ - endfunction is_multistage + ! computing stages + self%stage(1) = U + self%stage(2) = U * 0._R_P + do s=1, self%stages + self%stage(2) = (self%stage(2) * self%A(s)) + (self%stage(1)%t(t=t + self%C(s) * Dt) * Dt) + self%stage(1) = self%stage(1) + (self%stage(2) * self%B(s)) + enddo + U = self%stage(1) + if (present(new_Dt)) new_Dt = Dt + endsubroutine integrate - elemental function is_multistep(self) - !< Return .true. for multistage integrator. - class(integrator_runge_kutta_ls), intent(in) :: self !< Integrator. - logical :: is_multistep !< Inquire result. + subroutine integrate_fast(self, U, Dt, t, new_Dt) + !< Integrate field with explicit low storage Runge-Kutta scheme, fast mode. + class(integrator_runge_kutta_ls), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + real(R_P), intent(out), optional :: new_Dt !< New adapted time step. + integer(I_P) :: s !< First stages counter. - is_multistep = is_multistep_ - endfunction is_multistep + ! computing stages + self%stage(1) = U + call self%stage(2)%multiply_fast(lhs=U, rhs=0._R_P) + do s=1, self%stages + self%buffer = self%stage(1) + call self%buffer%t_fast(t=t + self%C(s) * Dt) + call self%buffer%multiply_fast(lhs=self%buffer, rhs=Dt) + call self%stage(2)%multiply_fast(lhs=self%stage(2), rhs=self%A(s)) + call self%stage(2)%add_fast(lhs=self%stage(2), rhs=self%buffer) + call self%buffer%multiply_fast(lhs=self%stage(2), rhs=self%B(s)) + call self%stage(1)%add_fast(lhs=self%stage(1), rhs=self%buffer) + enddo + U = self%stage(1) + if (present(new_Dt)) new_Dt = Dt + endsubroutine integrate_fast elemental function is_supported(self, scheme) !< Return .true. if the integrator class support the given scheme. @@ -284,22 +287,6 @@ elemental function is_supported(self, scheme) enddo endfunction is_supported - elemental function stages_number(self) - !< Return number of stages used. - class(integrator_runge_kutta_ls), intent(in) :: self !< Integrator. - integer(I_P) :: stages_number !< Number of stages used. - - stages_number = self%stages - endfunction stages_number - - elemental function steps_number(self) - !< Return number of steps used. - class(integrator_runge_kutta_ls), intent(in) :: self !< Integrator. - integer(I_P) :: steps_number !< Number of steps used. - - steps_number = 0 - endfunction steps_number - pure function supported_schemes(self) result(schemes) !< Return the list of supported schemes. class(integrator_runge_kutta_ls), intent(in) :: self !< Integrator. @@ -314,21 +301,22 @@ elemental subroutine destroy(self) !< Destroy the integrator. class(integrator_runge_kutta_ls), intent(inout) :: self !< Integrator. - call self%destroy_abstract - self%stages = 0 + call self%destroy_multistage if (allocated(self%A)) deallocate(self%A) if (allocated(self%B)) deallocate(self%B) if (allocated(self%C)) deallocate(self%C) endsubroutine destroy - subroutine initialize(self, scheme, stop_on_fail) + subroutine initialize(self, scheme, U, stop_on_fail) !< Create the actual RK integrator: initialize the Butcher' low storage table coefficients. class(integrator_runge_kutta_ls), intent(inout) :: self !< Integrator. character(*), intent(in) :: scheme !< Selected scheme. + class(integrand_object), intent(in), optional :: U !< Integrand molding prototype. logical, intent(in), optional :: stop_on_fail !< Stop execution if initialization fail. if (self%is_supported(scheme=scheme)) then call self%destroy + self%description_ = trim(adjustl(scheme)) select case(trim(adjustl(scheme))) case('runge_kutta_ls_stages_1_order_1') self%stages = 1 @@ -443,6 +431,8 @@ subroutine initialize(self, scheme, stop_on_fail) self%A(13) = -0.9514200470875948_R_P ; self%B(13) = 0.0780348340049386_R_P ; self%C(13) = 0.8627060376969976_R_P self%A(14) = -7.1151571693922548_R_P ; self%B(14) = 5.5059777270269628_R_P ; self%C(14) = 0.8734213127600976_R_P endselect + self%registers = 2 + if (present(U)) call self%allocate_integrand_members(U=U) else call self%trigger_error(error=ERROR_UNSUPPORTED_SCHEME, & error_message='"'//trim(adjustl(scheme))//'" unsupported scheme', & @@ -450,50 +440,6 @@ subroutine initialize(self, scheme, stop_on_fail) endif endsubroutine initialize - subroutine integrate(self, U, stage, Dt, t) - !< Integrate field with explicit low storage Runge-Kutta scheme. - class(integrator_runge_kutta_ls), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: stage(1:) !< Runge-Kutta registers. - real(R_P), intent(in) :: Dt !< Time step. - real(R_P), intent(in) :: t !< Time. - integer(I_P) :: s !< First stages counter. - - ! computing stages - stage(1) = U - stage(2) = U * 0._R_P - do s=1, self%stages - stage(2) = (stage(2) * self%A(s)) + (stage(1)%t(t=t + self%C(s) * Dt) * Dt) - stage(1) = stage(1) + (stage(2) * self%B(s)) - enddo - U = stage(1) - endsubroutine integrate - - subroutine integrate_fast(self, U, stage, buffer, Dt, t) - !< Integrate field with explicit low storage Runge-Kutta scheme, fast mode. - class(integrator_runge_kutta_ls), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: stage(1:) !< Runge-Kutta registers. - class(integrand_object), intent(inout) :: buffer !< Temporary buffer for doing fast operation. - real(R_P), intent(in) :: Dt !< Time step. - real(R_P), intent(in) :: t !< Time. - integer(I_P) :: s !< First stages counter. - - ! computing stages - stage(1) = U - call stage(2)%multiply_fast(lhs=U, rhs=0._R_P) - do s=1, self%stages - buffer = stage(1) - call buffer%t_fast(t=t + self%C(s) * Dt) - call buffer%multiply_fast(lhs=buffer, rhs=Dt) - call stage(2)%multiply_fast(lhs=stage(2), rhs=self%A(s)) - call stage(2)%add_fast(lhs=stage(2), rhs=buffer) - call buffer%multiply_fast(lhs=stage(2), rhs=self%B(s)) - call stage(1)%add_fast(lhs=stage(1), rhs=buffer) - enddo - U = stage(1) - endsubroutine integrate_fast - pure function registers_number() !< Return the number of registers used. integer(I_P) :: registers_number !< Number of registers used. diff --git a/src/lib/foodie_integrator_runge_kutta_lssp.f90 b/src/lib/foodie_integrator_runge_kutta_lssp.f90 index e58012bc..d062c470 100644 --- a/src/lib/foodie_integrator_runge_kutta_lssp.f90 +++ b/src/lib/foodie_integrator_runge_kutta_lssp.f90 @@ -64,8 +64,9 @@ module foodie_integrator_runge_kutta_lssp use foodie_error_codes, only : ERROR_UNSUPPORTED_SCHEME use foodie_integrand_object, only : integrand_object +use foodie_integrator_multistage_object, only : integrator_multistage_object use foodie_integrator_object, only : integrator_object -use penf, only : I_P, R_P +use penf, only : I_P, R_P, str implicit none private @@ -76,35 +77,27 @@ module foodie_integrator_runge_kutta_lssp trim(class_name_)//'_stages_s_order_s '] !< List of supported schemes. logical, parameter :: has_fast_mode_=.true. !< Flag to check if integrator provides *fast mode* integrate. -logical, parameter :: is_multistage_=.true. !< Flag to check if integrator is multistage. -logical, parameter :: is_multistep_=.false. !< Flag to check if integrator is multistep. -type, extends(integrator_object) :: integrator_runge_kutta_lssp +type, extends(integrator_multistage_object) :: integrator_runge_kutta_lssp !< FOODIE integrator: provide an explicit class of Linear SSP Runge-Kutta schemes, from 1st to s-th order accurate. !< !< @note The integrator must be created or initialized (initialize the RK coefficients) before used. private - integer(I_P) :: stages=0 !< Number of stages. real(R_P), allocatable :: alpha(:) !< \(\alpha\) coefficients. procedure(integrate_interface), pointer :: integrate_ => integrate_order_s_1 !< Integrate integrand field. procedure(integrate_fast_interface), pointer :: integrate_fast_ => integrate_order_s_1_fast !< Integrate integrand field, fast. contains ! deferred methods procedure, pass(self) :: class_name !< Return the class name of schemes. - procedure, pass(self) :: description !< Return pretty-printed object description. procedure, pass(self) :: has_fast_mode !< Return .true. if the integrator class has *fast mode* integrate. procedure, pass(lhs) :: integr_assign_integr !< Operator `=`. - procedure, pass(self) :: is_multistage !< Return .true. for multistage integrator. - procedure, pass(self) :: is_multistep !< Return .true. for multistep integrator. + procedure, pass(self) :: integrate !< Integrate integrand field. + procedure, pass(self) :: integrate_fast !< Integrate integrand field, fast mode. procedure, pass(self) :: is_supported !< Return .true. if the integrator class support the given scheme. - procedure, pass(self) :: stages_number !< Return number of stages used. - procedure, pass(self) :: steps_number !< Return number of steps used. procedure, pass(self) :: supported_schemes !< Return the list of supported schemes. ! public methods procedure, pass(self) :: destroy !< Destroy the integrator. procedure, pass(self) :: initialize !< Initialize (create) the integrator. - procedure, pass(self) :: integrate !< Integrate integrand field. - procedure, pass(self) :: integrate_fast !< Integrate integrand field, fast mode. ! private methods procedure, pass(self), private :: initialize_order_s_1 !< Integrate integrator for (s-1)-th order formula. procedure, pass(self), private :: initialize_order_s !< Integrate integrator for s-th order formula. @@ -114,25 +107,22 @@ module foodie_integrator_runge_kutta_lssp abstract interface !< Abstract interfaces of [[integrator_runge_kutta_lssp]] methods. - subroutine integrate_interface(self, U, stage, Dt, t) + subroutine integrate_interface(self, U, Dt, t) !< Integrate field with Linear SSP Runge-Kutta scheme. import :: integrand_object, integrator_runge_kutta_lssp, R_P - class(integrator_runge_kutta_lssp), intent(in) :: self !< Integrator. + class(integrator_runge_kutta_lssp), intent(inout) :: self !< Integrator. class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: stage(1:) !< Runge-Kutta stages [1:stages]. real(R_P), intent(in) :: Dt !< Time steps. real(R_P), intent(in) :: t !< Times. endsubroutine integrate_interface - subroutine integrate_fast_interface(self, U, stage, buffer, Dt, t) + subroutine integrate_fast_interface(self, U, Dt, t) !< Integrate field with Linear SSP Runge-Kutta scheme, fast mode. import :: integrand_object, integrator_runge_kutta_lssp, R_P - class(integrator_runge_kutta_lssp), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: stage(1:) !< Runge-Kutta stages [1:stages]. - class(integrand_object), intent(inout) :: buffer !< Temporary buffer for doing fast operation. - real(R_P), intent(in) :: Dt !< Time steps. - real(R_P), intent(in) :: t !< Times. + class(integrator_runge_kutta_lssp), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time steps. + real(R_P), intent(in) :: t !< Times. endsubroutine integrate_fast_interface endinterface @@ -146,25 +136,6 @@ pure function class_name(self) class_name = trim(adjustl(class_name_)) endfunction class_name - pure function description(self, prefix) result(desc) - !< Return a pretty-formatted object description. - class(integrator_runge_kutta_lssp), intent(in) :: self !< Integrator. - character(*), intent(in), optional :: prefix !< Prefixing string. - character(len=:), allocatable :: desc !< Description. - character(len=:), allocatable :: prefix_ !< Prefixing string, local variable. - character(len=1), parameter :: NL=new_line('a') !< New line character. - integer(I_P) :: s !< Counter. - - prefix_ = '' ; if (present(prefix)) prefix_ = prefix - desc = '' - desc = desc//prefix_//'Linear SSP Runge-Kutta multi-stage schemes class'//NL - desc = desc//prefix_//' Supported schemes:'//NL - do s=lbound(supported_schemes_, dim=1), ubound(supported_schemes_, dim=1) - 1 - desc = desc//prefix_//' + '//supported_schemes_(s)//NL - enddo - desc = desc//prefix_//' + '//supported_schemes_(ubound(supported_schemes_, dim=1)) - endfunction description - elemental function has_fast_mode(self) !< Return .true. if the integrator class has *fast mode* integrate. class(integrator_runge_kutta_lssp), intent(in) :: self !< Integrator. @@ -186,21 +157,31 @@ pure subroutine integr_assign_integr(lhs, rhs) endselect endsubroutine integr_assign_integr - elemental function is_multistage(self) - !< Return .true. for multistage integrator. - class(integrator_runge_kutta_lssp), intent(in) :: self !< Integrator. - logical :: is_multistage !< Inquire result. - - is_multistage = is_multistage_ - endfunction is_multistage - - elemental function is_multistep(self) - !< Return .true. for multistage integrator. - class(integrator_runge_kutta_lssp), intent(in) :: self !< Integrator. - logical :: is_multistep !< Inquire result. + subroutine integrate(self, U, Dt, t, new_Dt) + !< Integrate integrand field by Linear SSP Runge-Kutta methods. + !< + !< @note This method can be used **after** the integrator is created (i.e. the RK coefficients are initialized). + class(integrator_runge_kutta_lssp), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + real(R_P), intent(out), optional :: new_Dt !< New adapted time step. + + call self%integrate_(U=U, Dt=Dt, t=t) + if (present(new_Dt)) new_Dt = Dt + endsubroutine integrate - is_multistep = is_multistep_ - endfunction is_multistep + subroutine integrate_fast(self, U, Dt, t, new_Dt) + !< Integrate integrand field by Linear SSP Runge-Kutta methods. + class(integrator_runge_kutta_lssp), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + real(R_P), intent(out), optional :: new_Dt !< New adapted time step. + + call self%integrate_fast_(U=U, Dt=Dt, t=t) + if (present(new_Dt)) new_Dt = Dt + endsubroutine integrate_fast elemental function is_supported(self, scheme) !< Return .true. if the integrator class support the given scheme. @@ -218,22 +199,6 @@ elemental function is_supported(self, scheme) enddo endfunction is_supported - elemental function stages_number(self) - !< Return number of stages used. - class(integrator_runge_kutta_lssp), intent(in) :: self !< Integrator. - integer(I_P) :: stages_number !< Number of stages used. - - stages_number = self%stages - endfunction stages_number - - elemental function steps_number(self) - !< Return number of steps used. - class(integrator_runge_kutta_lssp), intent(in) :: self !< Integrator. - integer(I_P) :: steps_number !< Number of steps used. - - steps_number = 0 - endfunction steps_number - pure function supported_schemes(self) result(schemes) !< Return the list of supported schemes. class(integrator_runge_kutta_lssp), intent(in) :: self !< Integrator. @@ -248,16 +213,16 @@ elemental subroutine destroy(self) !< Destroy the integrator. class(integrator_runge_kutta_lssp), intent(inout) :: self !< Integrator. - call self%destroy_abstract - self%stages = 0 + call self%destroy_multistage if (allocated(self%alpha)) deallocate(self%alpha) self%integrate_ => integrate_order_s_1 endsubroutine destroy - subroutine initialize(self, scheme, stages, stop_on_fail) + subroutine initialize(self, scheme, U, stages, stop_on_fail) !< Create the actual RK integrator: initialize the Butcher' table coefficients. class(integrator_runge_kutta_lssp), intent(inout) :: self !< Integrator. character(*), intent(in) :: scheme !< Selected scheme. + class(integrand_object), intent(in), optional :: U !< Integrand molding prototype. integer(I_P), intent(in), optional :: stages !< Stages number. logical, intent(in), optional :: stop_on_fail !< Stop execution if initialization fail. @@ -283,6 +248,9 @@ subroutine initialize(self, scheme, stages, stop_on_fail) allocate(self%alpha(1:self%stages)) ; self%alpha = 0._R_P call self%initialize_order_s endselect + self%description_ = trim(adjustl(scheme))//'_stages_'//trim(str(self%stages)) + self%registers = self%stages + if (present(U)) call self%allocate_integrand_members(U=U) else call self%trigger_error(error=ERROR_UNSUPPORTED_SCHEME, & error_message='"'//trim(adjustl(scheme))//'" unsupported scheme', & @@ -290,31 +258,6 @@ subroutine initialize(self, scheme, stages, stop_on_fail) endif endsubroutine initialize - subroutine integrate(self, U, stage, Dt, t) - !< Integrate integrand field by Linear SSP Runge-Kutta methods. - !< - !< @note This method can be used **after** the integrator is created (i.e. the RK coefficients are initialized). - class(integrator_runge_kutta_lssp), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: stage(1:) !< Runge-Kutta stages [1:stages]. - real(R_P), intent(in) :: Dt !< Time step. - real(R_P), intent(in) :: t !< Time. - - call self%integrate_(U=U, stage=stage, Dt=Dt, t=t) - endsubroutine integrate - - subroutine integrate_fast(self, U, stage, buffer, Dt, t) - !< Integrate integrand field by Linear SSP Runge-Kutta methods. - class(integrator_runge_kutta_lssp), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: stage(1:) !< Runge-Kutta stages [1:stages]. - class(integrand_object), intent(inout) :: buffer !< Temporary buffer for doing fast operation. - real(R_P), intent(in) :: Dt !< Time step. - real(R_P), intent(in) :: t !< Time. - - call self%integrate_fast_(U=U, stage=stage, buffer=buffer, Dt=Dt, t=t) - endsubroutine integrate_fast - ! private methods elemental subroutine initialize_order_s_1(self) !< Initialize integrator for (s-1)-th order formula. @@ -357,110 +300,103 @@ elemental subroutine initialize_order_s(self) enddo enddo endassociate - endsubroutine initialize_order_s - subroutine integrate_order_s_1(self, U, stage, Dt, t) + subroutine integrate_order_s_1(self, U, Dt, t) !< Integrate integrand field by (s-1)-th order formula. - class(integrator_runge_kutta_lssp), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: stage(1:) !< Runge-Kutta stages [1:stages]. - real(R_P), intent(in) :: Dt !< Time step. - real(R_P), intent(in) :: t !< Time. - integer(I_P) :: s !< First stages counter. + class(integrator_runge_kutta_lssp), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + integer(I_P) :: s !< First stages counter. ! computing stages - stage(1) = U + self%stage(1) = U do s=2, self%stages - stage(s) = stage(s-1) + (stage(s-1)%t(t=t) * (Dt * 0.5_R_P)) + self%stage(s) = self%stage(s-1) + (self%stage(s-1)%t(t=t) * (Dt * 0.5_R_P)) enddo - stage(self%stages) = stage(self%stages) + (stage(self%stages)%t(t=t) * (Dt * 0.5_R_P)) + self%stage(self%stages) = self%stage(self%stages) + (self%stage(self%stages)%t(t=t) * (Dt * 0.5_R_P)) ! computing new time step U = U * 0._R_P do s=1, self%stages - U = U + (stage(s) * self%alpha(s)) + U = U + (self%stage(s) * self%alpha(s)) enddo endsubroutine integrate_order_s_1 - subroutine integrate_order_s(self, U, stage, Dt, t) + subroutine integrate_order_s(self, U, Dt, t) !< Integrate integrand field by s-th order formula. - class(integrator_runge_kutta_lssp), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: stage(1:) !< Runge-Kutta stages [1:stages]. - real(R_P), intent(in) :: Dt !< Time step. - real(R_P), intent(in) :: t !< Time. - integer(I_P) :: s !< First stages counter. + class(integrator_runge_kutta_lssp), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + integer(I_P) :: s !< First stages counter. ! computing stages - stage(1) = U + self%stage(1) = U do s=2, self%stages - stage(s) = stage(s-1) + (stage(s-1)%t(t=t) * Dt) + self%stage(s) = self%stage(s-1) + (self%stage(s-1)%t(t=t) * Dt) enddo - stage(self%stages) = stage(self%stages) + (stage(self%stages)%t(t=t) * Dt) + self%stage(self%stages) = self%stage(self%stages) + (self%stage(self%stages)%t(t=t) * Dt) ! computing new time step U = U * 0._R_P do s=1, self%stages - U = U + (stage(s) * self%alpha(s)) + U = U + (self%stage(s) * self%alpha(s)) enddo endsubroutine integrate_order_s - subroutine integrate_order_s_1_fast(self, U, stage, buffer, Dt, t) + subroutine integrate_order_s_1_fast(self, U, Dt, t) !< Integrate integrand field by (s-1)-th order formula. - class(integrator_runge_kutta_lssp), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: stage(1:) !< Runge-Kutta stages [1:stages]. - class(integrand_object), intent(inout) :: buffer !< Temporary buffer for doing fast operation. - real(R_P), intent(in) :: Dt !< Time step. - real(R_P), intent(in) :: t !< Time. - integer(I_P) :: s !< First stages counter. + class(integrator_runge_kutta_lssp), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + integer(I_P) :: s !< First stages counter. ! computing stages - stage(1) = U + self%stage(1) = U do s=2, self%stages - buffer = stage(s-1) - call buffer%t_fast(t=t) - call buffer%multiply_fast(lhs=buffer, rhs=Dt * 0.5_R_P) - call stage(s)%add_fast(lhs=stage(s-1), rhs=buffer) + self%buffer = self%stage(s-1) + call self%buffer%t_fast(t=t) + call self%buffer%multiply_fast(lhs=self%buffer, rhs=Dt * 0.5_R_P) + call self%stage(s)%add_fast(lhs=self%stage(s-1), rhs=self%buffer) enddo - buffer = stage(self%stages) - call buffer%t_fast(t=t) - call buffer%multiply_fast(lhs=buffer, rhs=Dt * 0.5_R_P) - call stage(self%stages)%add_fast(lhs=stage(self%stages), rhs=buffer) + self%buffer = self%stage(self%stages) + call self%buffer%t_fast(t=t) + call self%buffer%multiply_fast(lhs=self%buffer, rhs=Dt * 0.5_R_P) + call self%stage(self%stages)%add_fast(lhs=self%stage(self%stages), rhs=self%buffer) ! computing new time step call U%multiply_fast(lhs=U, rhs=0._R_P) do s=1, self%stages - call buffer%multiply_fast(lhs=stage(s), rhs=self%alpha(s)) - call U%add_fast(lhs=U, rhs=buffer) + call self%buffer%multiply_fast(lhs=self%stage(s), rhs=self%alpha(s)) + call U%add_fast(lhs=U, rhs=self%buffer) enddo endsubroutine integrate_order_s_1_fast - subroutine integrate_order_s_fast(self, U, stage, buffer, Dt, t) + subroutine integrate_order_s_fast(self, U, Dt, t) !< Integrate integrand field by s-th order formula, fast mode. - class(integrator_runge_kutta_lssp), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: stage(1:) !< Runge-Kutta stages [1:stages]. - class(integrand_object), intent(inout) :: buffer !< Temporary buffer for doing fast operation. - real(R_P), intent(in) :: Dt !< Time step. - real(R_P), intent(in) :: t !< Time. - integer(I_P) :: s !< First stages counter. + class(integrator_runge_kutta_lssp), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + integer(I_P) :: s !< First stages counter. ! computing stages - stage(1) = U + self%stage(1) = U do s=2, self%stages - buffer = stage(s-1) - call buffer%t_fast(t=t) - call buffer%multiply_fast(lhs=buffer, rhs=Dt) - call stage(s)%add_fast(lhs=stage(s-1), rhs=buffer) + self%buffer = self%stage(s-1) + call self%buffer%t_fast(t=t) + call self%buffer%multiply_fast(lhs=self%buffer, rhs=Dt) + call self%stage(s)%add_fast(lhs=self%stage(s-1), rhs=self%buffer) enddo - buffer = stage(self%stages) - call buffer%t_fast(t=t) - call buffer%multiply_fast(lhs=buffer, rhs=Dt) - call stage(self%stages)%add_fast(lhs=stage(self%stages), rhs=buffer) + self%buffer = self%stage(self%stages) + call self%buffer%t_fast(t=t) + call self%buffer%multiply_fast(lhs=self%buffer, rhs=Dt) + call self%stage(self%stages)%add_fast(lhs=self%stage(self%stages), rhs=self%buffer) ! computing new time step call U%multiply_fast(lhs=U, rhs=0._R_P) do s=1, self%stages - call buffer%multiply_fast(lhs=stage(s), rhs=self%alpha(s)) - call U%add_fast(lhs=U, rhs=buffer) + call self%buffer%multiply_fast(lhs=self%stage(s), rhs=self%alpha(s)) + call U%add_fast(lhs=U, rhs=self%buffer) enddo endsubroutine integrate_order_s_fast endmodule foodie_integrator_runge_kutta_lssp diff --git a/src/lib/foodie_integrator_runge_kutta_ssp.f90 b/src/lib/foodie_integrator_runge_kutta_ssp.f90 index e9bdc048..6803e44b 100644 --- a/src/lib/foodie_integrator_runge_kutta_ssp.f90 +++ b/src/lib/foodie_integrator_runge_kutta_ssp.f90 @@ -102,6 +102,7 @@ module foodie_integrator_runge_kutta_ssp use foodie_error_codes, only : ERROR_UNSUPPORTED_SCHEME use foodie_integrand_object, only : integrand_object +use foodie_integrator_multistage_object, only : integrator_multistage_object use foodie_integrator_object, only : integrator_object use penf, only : I_P, R_P @@ -116,39 +117,31 @@ module foodie_integrator_runge_kutta_ssp trim(class_name_)//'_stages_5_order_4'] !< List of supported schemes. logical, parameter :: has_fast_mode_=.true. !< Flag to check if integrator provides *fast mode* integrate. -logical, parameter :: is_multistage_=.true. !< Flag to check if integrator is multistage. -logical, parameter :: is_multistep_=.false. !< Flag to check if integrator is multistep. -type, extends(integrator_object) :: integrator_runge_kutta_ssp +type, extends(integrator_multistage_object) :: integrator_runge_kutta_ssp !< FOODIE integrator: provide an explicit class of SSP Runge-Kutta schemes, from 1st to 4th order accurate. !< !< @note The integrator must be created or initialized (initialize the RK coefficients) before used. private - integer(I_P) :: stages=0 !< Number of stages. real(R_P), allocatable :: alph(:,:) !< \(\alpha\) Butcher's coefficients. real(R_P), allocatable :: beta(:) !< \(\beta\) Butcher's coefficients. real(R_P), allocatable :: gamm(:) !< \(\gamma\) Butcher's coefficients. contains ! deferred methods procedure, pass(self) :: class_name !< Return the class name of schemes. - procedure, pass(self) :: description !< Return pretty-printed object description. procedure, pass(self) :: has_fast_mode !< Return .true. if the integrator class has *fast mode* integrate. procedure, pass(lhs) :: integr_assign_integr !< Operator `=`. - procedure, pass(self) :: is_multistage !< Return .true. for multistage integrator. - procedure, pass(self) :: is_multistep !< Return .true. for multistep integrator. + procedure, pass(self) :: integrate !< Integrate integrand field. + procedure, pass(self) :: integrate_fast !< Integrate integrand field, fast mode. procedure, pass(self) :: is_supported !< Return .true. if the integrator class support the given scheme. - procedure, pass(self) :: stages_number !< Return number of stages used. - procedure, pass(self) :: steps_number !< Return number of steps used. procedure, pass(self) :: supported_schemes !< Return the list of supported schemes. ! public methods - procedure, pass(self) :: destroy !< Destroy the integrator. - procedure, pass(self) :: initialize !< Initialize (create) the integrator. - procedure, pass(self) :: integrate !< Integrate integrand field. - procedure, pass(self) :: integrate_fast !< Integrate integrand field, fast mode. + procedure, pass(self) :: destroy !< Destroy the integrator. + procedure, pass(self) :: initialize !< Initialize (create) the integrator. endtype integrator_runge_kutta_ssp contains - ! deferred methods + ! deferred methods pure function class_name(self) !< Return the class name of schemes. class(integrator_runge_kutta_ssp), intent(in) :: self !< Integrator. @@ -157,25 +150,6 @@ pure function class_name(self) class_name = trim(adjustl(class_name_)) endfunction class_name - pure function description(self, prefix) result(desc) - !< Return a pretty-formatted object description. - class(integrator_runge_kutta_ssp), intent(in) :: self !< Integrator. - character(*), intent(in), optional :: prefix !< Prefixing string. - character(len=:), allocatable :: desc !< Description. - character(len=:), allocatable :: prefix_ !< Prefixing string, local variable. - character(len=1), parameter :: NL=new_line('a') !< New line character. - integer(I_P) :: s !< Counter. - - prefix_ = '' ; if (present(prefix)) prefix_ = prefix - desc = '' - desc = desc//prefix_//'SSP Runge-Kutta multi-stage schemes class'//NL - desc = desc//prefix_//' Supported schemes:'//NL - do s=lbound(supported_schemes_, dim=1), ubound(supported_schemes_, dim=1) - 1 - desc = desc//prefix_//' + '//supported_schemes_(s)//NL - enddo - desc = desc//prefix_//' + '//supported_schemes_(ubound(supported_schemes_, dim=1)) - endfunction description - elemental function has_fast_mode(self) !< Return .true. if the integrator class has *fast mode* integrate. class(integrator_runge_kutta_ssp), intent(in) :: self !< Integrator. @@ -199,21 +173,58 @@ pure subroutine integr_assign_integr(lhs, rhs) endselect endsubroutine integr_assign_integr - elemental function is_multistage(self) - !< Return .true. for multistage integrator. - class(integrator_runge_kutta_ssp), intent(in) :: self !< Integrator. - logical :: is_multistage !< Inquire result. + subroutine integrate(self, U, Dt, t, new_Dt) + !< Integrate field with explicit SSP Runge-Kutta scheme. + class(integrator_runge_kutta_ssp), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + real(R_P), optional, intent(out) :: new_Dt !< New adapted time step. + integer(I_P) :: s !< First stages counter. + integer(I_P) :: ss !< Second stages counter. - is_multistage = is_multistage_ - endfunction is_multistage + ! computing stages + do s=1, self%stages + self%stage(s) = U + do ss=1, s - 1 + self%stage(s) = self%stage(s) + (self%stage(ss) * (Dt * self%alph(s, ss))) + enddo + self%stage(s) = self%stage(s)%t(t=t + self%gamm(s) * Dt) + enddo + ! computing new time step + do s=1, self%stages + U = U + (self%stage(s) * (Dt * self%beta(s))) + enddo + if (present(new_Dt)) new_Dt = Dt + endsubroutine integrate - elemental function is_multistep(self) - !< Return .true. for multistage integrator. - class(integrator_runge_kutta_ssp), intent(in) :: self !< Integrator. - logical :: is_multistep !< Inquire result. + subroutine integrate_fast(self, U, Dt, t, new_Dt) + !< Integrate field with explicit SSP Runge-Kutta scheme. + class(integrator_runge_kutta_ssp), intent(inout) :: self !< Integrator. + class(integrand_object), intent(inout) :: U !< Field to be integrated. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: t !< Time. + real(R_P), optional, intent(out) :: new_Dt !< New adapted time step. + integer(I_P) :: s !< First stages counter. + integer(I_P) :: ss !< Second stages counter. - is_multistep = is_multistep_ - endfunction is_multistep + ! computing stages + self%buffer = U + do s=1, self%stages + self%stage(s) = U + do ss=1, s - 1 + call self%buffer%multiply_fast(lhs=self%stage(ss), rhs=Dt * self%alph(s, ss)) + call self%stage(s)%add_fast(lhs=self%stage(s), rhs=self%buffer) + enddo + call self%stage(s)%t_fast(t=t + self%gamm(s) * Dt) + enddo + ! computing new time step + do s=1, self%stages + call self%buffer%multiply_fast(lhs=self%stage(s), rhs=Dt * self%beta(s)) + call U%add_fast(lhs=U, rhs=self%buffer) + enddo + if (present(new_Dt)) new_Dt = Dt + endsubroutine integrate_fast elemental function is_supported(self, scheme) !< Return .true. if the integrator class support the given scheme. @@ -231,22 +242,6 @@ elemental function is_supported(self, scheme) enddo endfunction is_supported - elemental function stages_number(self) - !< Return number of stages used. - class(integrator_runge_kutta_ssp), intent(in) :: self !< Integrator. - integer(I_P) :: stages_number !< Number of stages used. - - stages_number = self%stages - endfunction stages_number - - elemental function steps_number(self) - !< Return number of steps used. - class(integrator_runge_kutta_ssp), intent(in) :: self !< Integrator. - integer(I_P) :: steps_number !< Number of steps used. - - steps_number = 0 - endfunction steps_number - pure function supported_schemes(self) result(schemes) !< Return the list of supported schemes. class(integrator_runge_kutta_ssp), intent(in) :: self !< Integrator. @@ -261,21 +256,22 @@ elemental subroutine destroy(self) !< Destroy the integrator. class(integrator_runge_kutta_ssp), intent(inout) :: self !< Integrator. - call self%destroy_abstract - self%stages = 0 + call self%destroy_multistage if (allocated(self%alph)) deallocate(self%alph) if (allocated(self%beta)) deallocate(self%beta) if (allocated(self%gamm)) deallocate(self%gamm) endsubroutine destroy - subroutine initialize(self, scheme, stop_on_fail) + subroutine initialize(self, scheme, U, stop_on_fail) !< Create the actual RK integrator: initialize the Butcher' table coefficients. class(integrator_runge_kutta_ssp), intent(inout) :: self !< Integrator. character(*), intent(in) :: scheme !< Selected scheme. + class(integrand_object), intent(in), optional :: U !< Integrand molding prototype. logical, intent(in), optional :: stop_on_fail !< Stop execution if initialization fail. if (self%is_supported(scheme=scheme)) then call self%destroy + self%description_ = trim(adjustl(scheme)) select case(trim(adjustl(scheme))) case('runge_kutta_ssp_stages_1_order_1') self%stages = 1 @@ -334,62 +330,12 @@ subroutine initialize(self, scheme, stop_on_fail) self%gamm(4) = 0.47454236302687_R_P self%gamm(5) = 0.93501063100924_R_P endselect + self%registers = self%stages + if (present(U)) call self%allocate_integrand_members(U=U) else call self%trigger_error(error=ERROR_UNSUPPORTED_SCHEME, & error_message='"'//trim(adjustl(scheme))//'" unsupported scheme', & is_severe=stop_on_fail) endif endsubroutine initialize - - subroutine integrate(self, U, stage, Dt, t) - !< Integrate field with explicit SSP Runge-Kutta scheme. - class(integrator_runge_kutta_ssp), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: stage(1:) !< Runge-Kutta stages [1:stages]. - real(R_P), intent(in) :: Dt !< Time step. - real(R_P), intent(in) :: t !< Time. - integer(I_P) :: s !< First stages counter. - integer(I_P) :: ss !< Second stages counter. - - ! computing stages - do s=1, self%stages - stage(s) = U - do ss=1, s - 1 - stage(s) = stage(s) + (stage(ss) * (Dt * self%alph(s, ss))) - enddo - stage(s) = stage(s)%t(t=t + self%gamm(s) * Dt) - enddo - ! computing new time step - do s=1, self%stages - U = U + (stage(s) * (Dt * self%beta(s))) - enddo - endsubroutine integrate - - subroutine integrate_fast(self, U, stage, buffer, Dt, t) - !< Integrate field with explicit SSP Runge-Kutta scheme. - class(integrator_runge_kutta_ssp), intent(in) :: self !< Integrator. - class(integrand_object), intent(inout) :: U !< Field to be integrated. - class(integrand_object), intent(inout) :: stage(1:) !< Runge-Kutta stages [1:stages]. - class(integrand_object), intent(inout) :: buffer !< Temporary buffer for doing fast operation. - real(R_P), intent(in) :: Dt !< Time step. - real(R_P), intent(in) :: t !< Time. - integer(I_P) :: s !< First stages counter. - integer(I_P) :: ss !< Second stages counter. - - ! computing stages - buffer = U - do s=1, self%stages - stage(s) = U - do ss=1, s - 1 - call buffer%multiply_fast(lhs=stage(ss), rhs=Dt * self%alph(s, ss)) - call stage(s)%add_fast(lhs=stage(s), rhs=buffer) - enddo - call stage(s)%t_fast(t=t + self%gamm(s) * Dt) - enddo - ! computing new time step - do s=1, self%stages - call buffer%multiply_fast(lhs=stage(s), rhs=Dt * self%beta(s)) - call U%add_fast(lhs=U, rhs=buffer) - enddo - endsubroutine integrate_fast endmodule foodie_integrator_runge_kutta_ssp diff --git a/src/tests/lcce/foodie_test_integrand_lcce.f90 b/src/tests/lcce/foodie_test_integrand_lcce.f90 index 66a0e1b2..ca935f00 100644 --- a/src/tests/lcce/foodie_test_integrand_lcce.f90 +++ b/src/tests/lcce/foodie_test_integrand_lcce.f90 @@ -6,7 +6,7 @@ module foodie_test_integrand_lcce !< abstract integrand type. use foodie, only : integrand_object -use penf, only : R_P, I_P +use penf, only : I_P, R_P implicit none private @@ -47,8 +47,8 @@ module foodie_test_integrand_lcce real(R_P) :: U0=0._R_P !< Integrand initial state. contains ! auxiliary methods - procedure, pass(self), public :: initialize !< Initialize integrand. procedure, pass(self), public :: exact_solution !< Return exact solution. + procedure, pass(self), public :: initialize !< Initialize integrand. procedure, pass(self), public :: output !< Extract integrand state field. ! public deferred methods procedure, pass(self), public :: integrand_dimension !< Return integrand dimension. @@ -82,18 +82,6 @@ module foodie_test_integrand_lcce contains ! auxiliary methods - pure subroutine initialize(self, a, b, U0) - !< Initialize integrand. - class(integrand_lcce), intent(inout) :: self !< Integrand. - real(R_P), intent(in) :: a, b !< Equation coefficients. - real(R_P), intent(in) :: U0 !< Initial state of the integrand. - - self%a = a - self%b = b - self%U = U0 - self%U0 = U0 - endsubroutine initialize - pure function exact_solution(self, t, t0) result(exact) !< Return exact solution. class(integrand_lcce), intent(in) :: self !< Integrand. @@ -106,6 +94,18 @@ pure function exact_solution(self, t, t0) result(exact) exact = (self%U0 + self%b / self%a) * exp(self%a * (t - t0_)) - self%b / self%a endfunction exact_solution + pure subroutine initialize(self, a, b, U0) + !< Initialize integrand. + class(integrand_lcce), intent(inout) :: self !< Integrand. + real(R_P), intent(in) :: a, b !< Equation coefficients. + real(R_P), intent(in) :: U0 !< Initial state of the integrand. + + self%a = a + self%b = b + self%U = U0 + self%U0 = U0 + endsubroutine initialize + pure function output(self) result(state) !< Extract integrand state field. class(integrand_lcce), intent(in) :: self !< Integrand. @@ -118,7 +118,7 @@ pure function output(self) result(state) pure function integrand_dimension(self) !< return integrand dimension. class(integrand_lcce), intent(in) :: self !< integrand. - integer(i_p) :: integrand_dimension !< integrand dimension. + integer(I_P) :: integrand_dimension !< integrand dimension. integrand_dimension = 1 endfunction integrand_dimension diff --git a/src/tests/tester/fobos b/src/tests/tester/fobos new file mode 100644 index 00000000..1a50ae85 --- /dev/null +++ b/src/tests/tester/fobos @@ -0,0 +1,66 @@ +[modes] +modes = gnu gnu-debug gnu-coverage + intel intel-debug + +[common-variables] +$CSTATIC_GNU = -cpp -c -frealloc-lhs +$CSTATIC_INT = -cpp -c -assume realloc_lhs +$DEBUG_GNU = -Og -g3 -Warray-bounds -Wcharacter-truncation -Wline-truncation -Wimplicit-interface -Wimplicit-procedure -Wunderflow -Wuninitialized -fcheck=all -fmodule-private -ffree-line-length-132 -fimplicit-none -fbacktrace -fdump-core -finit-real=nan -std=f2008 -fall-intrinsics +$DEBUG_INT = -O0 -debug all -check all -warn all -extend-source 132 -traceback -gen-interfaces -fp-stack-check -fstack-protector-all -ftrapuv -no-ftz -std08 +$OPTIMIZE = -O2 +$EXDIRS = src/tests/parallel/ src/tests/quarantena/ src/tests/regression/ + PENF/src/tests/ + FACE/src/tests/ FACE/src/third_party/ + FLAP/src/tests/ FLAP/src/third_party/ + WenOOF/src/tests/ WenOOF/src/third_party/ + +# main modes +[gnu] +help = Build test in release mode with GNU gfortran +compiler = gnu +cflags = $CSTATIC_GNU $OPTIMIZE +lflags = $OPTIMIZE +template = template-common + +[gnu-debug] +help = Build test in debug mode with GNU gfortran +compiler = gnu +cflags = $CSTATIC_GNU $DEBUG_GNU +lflags = $DEBUG_GNU +template = template-common + +[gnu-coverage] +help = Build test in release coverage mode with GNU gfortran +compiler = gnu +cflags = $CSTATIC_GNU $DEBUG_GNU +lflags = $DEBUG_GNU +coverage = True +template = template-common + +[intel] +help = Build test in release mode with Intel Fortran +compiler = intel +cflags = $CSTATIC_INT $OPTIMIZE +lflags = $OPTIMIZE +template = template-common + +[intel-debug] +help = Build test in debug mode with Intel Fortran +compiler = intel +cflags = $CSTATIC_INT $DEBUG_INT +lflags = $DEBUG_INT +template = template-common + +# templates +[template-common] +cflags_heritage = True +build_dir = build/tests/tester/ +mod_dir = mod +obj_dir = obj +src = src/ +target = src/tests/tester/foodie_tester.f90 +exclude_dirs = $EXDIRS +colors = True +quiet = False +log = True +jobs = 10 diff --git a/src/tests/tester/foodie_test_integrand_ladvection.f90 b/src/tests/tester/foodie_test_integrand_ladvection.f90 new file mode 100644 index 00000000..0f7dc959 --- /dev/null +++ b/src/tests/tester/foodie_test_integrand_ladvection.f90 @@ -0,0 +1,520 @@ +!< Define [[integrand_ladvection]], the 1D linear advection PDE test field that is a concrete extension of the +!< abstract integrand type. + +module foodie_test_integrand_ladvection +!< Define [[integrand_ladvection]], the 1D linear advection PDE test field that is a concrete extension of the +!< abstract integrand type. + +use, intrinsic :: iso_fortran_env, only : stderr=>error_unit +use flap, only : command_line_interface +use foodie, only : integrand_object +use foodie_test_integrand_tester_object, only : integrand_tester_object +use penf, only : FR_P, I_P, R_P, str, strz +use wenoof, only : interpolator_object, wenoof_create + +implicit none +private +public :: integrand_ladvection + +type, extends(integrand_tester_object) :: integrand_ladvection + !< 1D linear advection field. + !< + !< It is a FOODIE integrand class concrete extension. + !< + !<### 1D linear advection field + !< The 1D linear advection equation is a conservation law that reads as + !<$$ + !<\begin{matrix} + ! compute_dt !< Compute the current time step, by means of CFL condition. + procedure, pass(self), public :: exact_solution !< Return exact solution. + procedure, pass(self), public :: output !< Extract integrand state field. + ! integrand_tester_object deferred methods + procedure, pass(self), public :: description !< Return an informative description of the test. + procedure, pass(self), public :: export_tecplot !< Export integrand to Tecplot file. + procedure, pass(self), public :: parse_cli !< Initialize from command line interface. + procedure, nopass, public :: set_cli !< Set command line interface. + ! integrand_object deferred methods + procedure, pass(self), public :: integrand_dimension !< Return integrand dimension. + procedure, pass(self), public :: t => dU_dt !< Time derivative, residuals. + ! operators + procedure, pass(lhs), public :: local_error !<`||integrand_ladvection - integrand_ladvection||` operator. + ! + + procedure, pass(lhs), public :: integrand_add_integrand !< `+` operator. + procedure, pass(lhs), public :: integrand_add_real !< `+ real` operator. + procedure, pass(rhs), public :: real_add_integrand !< `real +` operator. + ! * + procedure, pass(lhs), public :: integrand_multiply_integrand !< `*` operator. + procedure, pass(lhs), public :: integrand_multiply_real !< `* real` operator. + procedure, pass(rhs), public :: real_multiply_integrand !< `real *` operator. + procedure, pass(lhs), public :: integrand_multiply_real_scalar !< `* real_scalar` operator. + procedure, pass(rhs), public :: real_scalar_multiply_integrand !< `real_scalar *` operator. + ! - + procedure, pass(lhs), public :: integrand_sub_integrand !< `-` operator. + procedure, pass(lhs), public :: integrand_sub_real !< `- real` operator. + procedure, pass(rhs), public :: real_sub_integrand !< `real -` operator. + ! = + procedure, pass(lhs), public :: assign_integrand !< `=` operator. + procedure, pass(lhs), public :: assign_real !< `= real` operator. + ! override fast operators + ! procedure, pass(self), public :: t_fast !< Time derivative, residuals, fast mode. + ! procedure, pass(opr), public :: integrand_add_integrand_fast !< `+` fast operator. + ! procedure, pass(opr), public :: integrand_multiply_integrand_fast !< `*` fast operator. + ! procedure, pass(opr), public :: integrand_multiply_real_scalar_fast !< `* real_scalar` fast operator. + ! procedure, pass(opr), public :: integrand_subtract_integrand_fast !< `-` fast operator. + ! private methods + procedure, pass(self), private :: impose_boundary_conditions !< Impose boundary conditions. + procedure, pass(self), private :: reconstruct_interfaces !< Reconstruct interface states. + procedure, pass(self), private :: set_square_wave_initial_state !< Set initial state as a square wave. +endtype integrand_ladvection + +contains + ! auxiliary methods + pure subroutine destroy(self) + !< Destroy field. + class(integrand_ladvection), intent(inout) :: self !< Advection field. + type(integrand_ladvection) :: fresh !< Fresh field to reset self. + + self = fresh + endsubroutine destroy + + pure function compute_dt(self, final_time, t) result(Dt) + !< Compute the current time step by means of CFL condition. + class(integrand_ladvection), intent(in) :: self !< Advection field. + real(R_P), intent(in) :: final_time !< Maximum integration time. + real(R_P), intent(in), optional :: t !< Time. + real(R_P) :: Dt !< Time step. + + associate(a=>self%a, Ni=>self%Ni, Dx=>self%Dx, CFL=>self%CFL) + Dt = Dx * CFL / abs(a) + if (present(t)) then + if ((t + Dt) > final_time) Dt = final_time - t + endif + endassociate + endfunction compute_dt + + pure function exact_solution(self, u0, t) result(exact) + !< Return exact solution. + class(integrand_ladvection), intent(in) :: self !< Integrand. + real(R_P), intent(in) :: u0(1:) !< Initial state + real(R_P), intent(in) :: t !< Time. + real(R_P), allocatable :: exact(:) !< Exact solution. + + endfunction exact_solution + + pure function output(self) result(state) + !< Output the advection field state. + class(integrand_ladvection), intent(in) :: self !< Advection field. + real(R_P), allocatable :: state(:) !< Advection state + + state = self%u(1:self%Ni) + endfunction output + + ! integrand_tester_object deferred methods + pure function description(self, prefix) result(desc) + !< Return informative integrator description. + class(integrand_ladvection), intent(in) :: self !< Integrator. + character(*), intent(in), optional :: prefix !< Prefixing string. + character(len=:), allocatable :: desc !< Description. + character(len=:), allocatable :: prefix_ !< Prefixing string, local variable. + + prefix_ = '' ; if (present(prefix)) prefix_ = prefix + desc = prefix//'linear_advection-Ni_'//trim(strz(self%Ni, 10)) + endfunction description + + subroutine export_tecplot(self, file_name, t, scheme, close_file) + !< Export integrand to Tecplot file. + class(integrand_ladvection), intent(in) :: self !< Advection field. + character(*), intent(in), optional :: file_name !< File name. + real(R_P), intent(in), optional :: t !< Time. + character(*), intent(in), optional :: scheme !< Scheme used to integrate integrand. + logical, intent(in), optional :: close_file !< Flag for closing file. + logical, save :: is_open=.false. !< Flag for checking if file is open. + integer(I_P), save :: file_unit !< File unit. + integer(I_P) :: i !< Counter. + + if (present(close_file)) then + if (close_file .and. is_open) then + close(unit=file_unit) + is_open = .false. + endif + else + if (present(file_name)) then + if (is_open) close(unit=file_unit) + open(newunit=file_unit, file=trim(adjustl(file_name))) + is_open = .true. + write(unit=file_unit, fmt='(A)') 'VARIABLES="x" "u"' + endif + if (present(t) .and. present(scheme) .and. is_open) then + write(unit=file_unit, fmt='(A)') 'ZONE T="'//str(t)//' '//trim(adjustl(scheme))//'"' + do i=1, self%Ni + write(unit=file_unit, fmt='(2('//FR_P//',1X))') self%Dx * i - 0.5_R_P * self%Dx, self%u(i) + enddo + endif + endif + endsubroutine export_tecplot + + subroutine parse_cli(self, cli) + !< Initialize from command line interface. + class(integrand_ladvection), intent(inout) :: self !< Advection field. + type(command_line_interface), intent(inout) :: cli !< Command line interface handler. + character(99) :: initial_state !< Initial state. + + call self%destroy + + call cli%get(switch='--cfl', val=self%CFL, error=cli%error) ; if (cli%error/=0) stop + call cli%get(switch='--w-scheme', val=self%w_scheme, error=cli%error) ; if (cli%error/=0) stop + call cli%get(switch='--weno-order', val=self%weno_order, error=cli%error) ; if (cli%error/=0) stop + call cli%get(switch='--weno-eps', val=self%weno_eps, error=cli%error) ; if (cli%error/=0) stop + call cli%get(switch='-a', val=self%a, error=cli%error) ; if (cli%error/=0) stop + call cli%get(switch='--length', val=self%length, error=cli%error) ; if (cli%error/=0) stop + call cli%get(switch='--Ni', val=self%Ni, error=cli%error) ; if (cli%error/=0) stop + call cli%get(switch='-is', val=initial_state, error=cli%error) ; if (cli%error/=0) stop + + self%Ng = (self%weno_order + 1) / 2 + self%Dx = self%length / self%Ni + + select case(trim(adjustl(initial_state))) + case('square_wave') + call self%set_square_wave_initial_state + endselect + + if (self%weno_order>1) call wenoof_create(interpolator_type=trim(adjustl(self%w_scheme)), & + S=self%Ng, & + interpolator=self%interpolator, & + eps=self%weno_eps) + endsubroutine parse_cli + + subroutine set_cli(cli) + !< Set command line interface. + type(command_line_interface), intent(inout) :: cli !< Command line interface handler. + + call cli%add(switch='--w-scheme', help='WENO scheme', required=.false., act='store', def='reconstructor-JS', & + choices='reconstructor-JS,reconstructor-M-JS,reconstructor-M-Z,reconstructor-Z') + call cli%add(switch='--weno-order', help='WENO order', required=.false., act='store', def='1') + call cli%add(switch='--weno-eps', help='WENO epsilon parameter', required=.false., act='store', def='0.000001') + call cli%add(switch='--cfl', help='CFL value', required=.false., act='store', def='0.8') + call cli%add(switch='-a', help='advection coefficient', required=.false., act='store', def='1.0') + call cli%add(switch='--length', help='domain lenth', required=.false., act='store', def='1.0') + call cli%add(switch='--Ni', help='number finite volumes used', required=.false., act='store', def='100') + call cli%add(switch='--initial_state', switch_ab='-is', help='initial state', required=.false., act='store', & + def='square_wave', choices='square_wave') + endsubroutine set_cli + + ! integrand_object deferred methods + function dU_dt(self, t) result(dState_dt) + !< Time derivative of advection field, the residuals function. + class(integrand_ladvection), intent(in) :: self !< Advection field. + real(R_P), intent(in), optional :: t !< Time. + real(R_P), allocatable :: dState_dt(:) !< Advection field time derivative. + real(R_P) :: u(1-self%Ng:self%Ni+self%Ng) !< Conservative variable. + real(R_P) :: ur(1:2,0:self%Ni+1) !< Reconstructed conservative variable. + real(R_P) :: f(0:self%Ni) !< Flux of conservative variable. + integer(I_P) :: i !< Counter. + + do i=1, self%Ni + U(i) = self%U(i) + enddo + call self%impose_boundary_conditions(u=u) + call self%reconstruct_interfaces(conservative=u, r_conservative=ur) + do i=0, self%Ni + call solve_riemann_problem(state_left=ur(2, i), state_right=ur(1, i+1), flux=f(i)) + enddo + allocate(dState_dt(1-self%Ng:self%Ni+self%Ng)) + do i=1, self%Ni + dState_dt(i) = (f(i - 1) - f(i)) / self%Dx + enddo + contains + subroutine solve_riemann_problem(state_left, state_right, flux) + !< Solver Riemann problem of linear advection by upwinding. + real(R_P), intent(in) :: state_left !< Left state. + real(R_P), intent(in) :: state_right !< right state. + real(R_P), intent(out) :: flux !< Flux of conservative variable. + + if (self%a > 0._R_P) then + flux = self%a * state_left + else + flux = self%a * state_right + endif + endsubroutine solve_riemann_problem + endfunction dU_dt + + pure function integrand_dimension(self) + !< return integrand dimension. + class(integrand_ladvection), intent(in) :: self !< integrand. + integer(I_P) :: integrand_dimension !< integrand dimension. + + integrand_dimension = self%Ni + endfunction integrand_dimension + + function local_error(lhs, rhs) result(error) + !< Estimate local truncation error between 2 advection approximations. + !< + !< The estimation is done by norm L2 of U: + !< + !< $$ error = \sqrt{ \sum_i{\sum_i{ \frac{(lhs\%u_i - rhs\%u_i)^2}{lhs\%u_i^2} }} } $$ + class(integrand_ladvection), intent(in) :: lhs !< Left hand side. + class(integrand_object), intent(in) :: rhs !< Right hand side. + real(R_P) :: error !< Error estimation. + integer(I_P) :: i !< Space counter. + + select type(rhs) + class is (integrand_ladvection) + error = 0._R_P + do i=1, lhs%Ni + error = error + (lhs%u(i) - rhs%u(i)) ** 2 + enddo + error = sqrt(error) + endselect + endfunction local_error + + ! + + pure function integrand_add_integrand(lhs, rhs) result(opr) + !< `+` operator. + class(integrand_ladvection), intent(in) :: lhs !< Left hand side. + class(integrand_object), intent(in) :: rhs !< Right hand side. + real(R_P), allocatable :: opr(:) !< Operator result. + + select type(rhs) + class is(integrand_ladvection) + opr = lhs%u + rhs%u + endselect + endfunction integrand_add_integrand + + pure function integrand_add_real(lhs, rhs) result(opr) + !< `+ real` operator. + class(integrand_ladvection), intent(in) :: lhs !< Left hand side. + real(R_P), intent(in) :: rhs(1:) !< Right hand side. + real(R_P), allocatable :: opr(:) !< Operator result. + + opr = lhs%u + rhs + endfunction integrand_add_real + + pure function real_add_integrand(lhs, rhs) result(opr) + !< `real +` operator. + real(R_P), intent(in) :: lhs(1:) !< Left hand side. + class(integrand_ladvection), intent(in) :: rhs !< Left hand side. + real(R_P), allocatable :: opr(:) !< Operator result. + + opr = lhs + rhs%U + endfunction real_add_integrand + + ! * + pure function integrand_multiply_integrand(lhs, rhs) result(opr) + !< `*` operator. + class(integrand_ladvection), intent(in) :: lhs !< Left hand side. + class(integrand_object), intent(in) :: rhs !< Right hand side. + real(R_P), allocatable :: opr(:) !< Operator result. + + select type(rhs) + class is(integrand_ladvection) + opr = lhs%U * rhs%U + endselect + endfunction integrand_multiply_integrand + + pure function integrand_multiply_real(lhs, rhs) result(opr) + !< `* real_scalar` operator. + class(integrand_ladvection), intent(in) :: lhs !< Left hand side. + real(R_P), intent(in) :: rhs(1:) !< Right hand side. + real(R_P), allocatable :: opr(:) !< Operator result. + + opr = lhs%U * rhs + endfunction integrand_multiply_real + + pure function real_multiply_integrand(lhs, rhs) result(opr) + !< `real_scalar *` operator. + class(integrand_ladvection), intent(in) :: rhs !< Right hand side. + real(R_P), intent(in) :: lhs(1:) !< Left hand side. + real(R_P), allocatable :: opr(:) !< Operator result. + + opr = lhs * rhs%U + endfunction real_multiply_integrand + + pure function integrand_multiply_real_scalar(lhs, rhs) result(opr) + !< `* real_scalar` operator. + class(integrand_ladvection), intent(in) :: lhs !< Left hand side. + real(R_P), intent(in) :: rhs !< Right hand side. + real(R_P), allocatable :: opr(:) !< Operator result. + + opr = lhs%U * rhs + endfunction integrand_multiply_real_scalar + + pure function real_scalar_multiply_integrand(lhs, rhs) result(opr) + !< `real_scalar *` operator. + real(R_P), intent(in) :: lhs !< Left hand side. + class(integrand_ladvection), intent(in) :: rhs !< Right hand side. + real(R_P), allocatable :: opr(:) !< Operator result. + + opr = lhs * rhs%U + endfunction real_scalar_multiply_integrand + + ! - + pure function integrand_sub_integrand(lhs, rhs) result(opr) + !< `-` operator. + class(integrand_ladvection), intent(in) :: lhs !< Left hand side. + class(integrand_object), intent(in) :: rhs !< Right hand side. + real(R_P), allocatable :: opr(:) !< Operator result. + + select type(rhs) + class is(integrand_ladvection) + opr = lhs%U - rhs%U + endselect + endfunction integrand_sub_integrand + + pure function integrand_sub_real(lhs, rhs) result(opr) + !< `- real` operator. + class(integrand_ladvection), intent(in) :: lhs !< Left hand side. + real(R_P), intent(in) :: rhs(1:) !< Right hand side. + real(R_P), allocatable :: opr(:) !< Operator result. + + opr = lhs%U - rhs + endfunction integrand_sub_real + + pure function real_sub_integrand(lhs, rhs) result(opr) + !< `real -` operator. + real(R_P), intent(in) :: lhs(1:) !< Left hand side. + class(integrand_ladvection), intent(in) :: rhs !< Left hand side. + real(R_P), allocatable :: opr(:) !< Operator result. + + opr = lhs - rhs%U + endfunction real_sub_integrand + + ! = + pure subroutine assign_integrand(lhs, rhs) + !< `=` operator. + class(integrand_ladvection), intent(inout) :: lhs !< Left hand side. + class(integrand_object), intent(in) :: rhs !< Right hand side. + + select type(rhs) + class is(integrand_ladvection) + lhs%w_scheme = rhs%w_scheme + lhs%weno_order = rhs%weno_order + lhs%weno_eps = rhs%weno_eps + lhs%CFL = rhs%CFL + lhs%Ni = rhs%Ni + lhs%Ng = rhs%Ng + lhs%Dx = rhs%Dx + lhs%a = rhs%a + if (allocated(rhs%u)) then + lhs%u = rhs%u + else + if (allocated(lhs%u)) deallocate(lhs%u) + endif + if (allocated(rhs%interpolator)) then + if (allocated(lhs%interpolator)) deallocate(lhs%interpolator) + allocate(lhs%interpolator, mold=rhs%interpolator) + lhs%interpolator = rhs%interpolator + else + if (allocated(lhs%interpolator)) deallocate(lhs%interpolator) + endif + endselect + endsubroutine assign_integrand + + pure subroutine assign_real(lhs, rhs) + !< Assign one real to an advection field. + class(integrand_ladvection), intent(inout) :: lhs !< Left hand side. + real(R_P), intent(in) :: rhs(1:) !< Right hand side. + + lhs%u = rhs + endsubroutine assign_real + + ! private methods + pure subroutine impose_boundary_conditions(self, u) + !< Impose boundary conditions. + class(integrand_ladvection), intent(in) :: self !< Advection field. + real(R_P), intent(inout) :: u(1-self%Ng:) !< Conservative variables. + integer(I_P) :: i !< Space counter. + + do i=1-self%Ng, 0 + u(i) = u(self%Ni+i) + enddo + + do i=self%Ni+1, self%Ni+self%Ng + u(i) = u(i-self%Ni) + enddo + endsubroutine impose_boundary_conditions + + subroutine reconstruct_interfaces(self, conservative, r_conservative) + !< Reconstruct interfaces states. + class(integrand_ladvection), intent(in) :: self !< Advection field. + real(R_P), intent(in) :: conservative(1-self%Ng:) !< Conservative variables. + real(R_P), intent(inout) :: r_conservative(1:, 0:) !< Reconstructed conservative vars. + real(R_P), allocatable :: U(:) !< Serialized conservative variables. + real(R_P) :: C(1:2, 1-self%Ng:-1+self%Ng) !< Stencils. + real(R_P) :: CR(1:2) !< Reconstrcuted intrafaces. + integer(I_P) :: i !< Counter. + integer(I_P) :: j !< Counter. + integer(I_P) :: f !< Counter. + + select case(self%weno_order) + case(1) ! 1st order piecewise constant reconstruction + do i=0, self%Ni+1 + r_conservative(1, i) = conservative(i) + r_conservative(2, i) = r_conservative(1, i) + enddo + case(3, 5, 7, 9, 11, 13, 15, 17) ! 3rd-17th order WENO reconstruction + do i=0, self%Ni+1 + do j=i+1-self%Ng, i-1+self%Ng + do f=1, 2 + C(f, j-i) = conservative(j) + enddo + enddo + call self%interpolator%interpolate(stencil=C(:, :), interpolation=CR(:)) + do f=1, 2 + r_conservative(f, i) = CR(f) + enddo + enddo + endselect + endsubroutine reconstruct_interfaces + + subroutine set_square_wave_initial_state(self) + !< Set initial state as a square wave. + class(integrand_ladvection), intent(inout) :: self !< Advection field. + real(R_P) :: x(1:self%ni) !< Cell center x-abscissa values. + integer(I_P) :: i !< Space counter. + + if (allocated(self%u)) deallocate(self%u) ; allocate(self%u(1-self%Ng:self%Ni+self%Ng)) + do i=1, self%Ni + x(i) = self%Dx * i - 0.5_R_P * self%Dx + if (x(i) < 0.25_R_P) then + self%u(i) = 0._R_P + elseif (0.25_R_P <= x(i) .and. x(i) < 0.75_R_P) then + self%u(i) = 1._R_P + else + self%u(i) = 0._R_P + endif + enddo + endsubroutine set_square_wave_initial_state +endmodule foodie_test_integrand_ladvection diff --git a/src/tests/oscillation/foodie_test_integrand_oscillation.f90 b/src/tests/tester/foodie_test_integrand_oscillation.f90 similarity index 57% rename from src/tests/oscillation/foodie_test_integrand_oscillation.f90 rename to src/tests/tester/foodie_test_integrand_oscillation.f90 index a7d6820b..c0d932c6 100644 --- a/src/tests/oscillation/foodie_test_integrand_oscillation.f90 +++ b/src/tests/tester/foodie_test_integrand_oscillation.f90 @@ -3,95 +3,101 @@ module foodie_test_integrand_oscillation !< Define [[integrand_oscillation]], the Oscillation test field that is a concrete extension of the abstract integrand type. +use flap, only : command_line_interface use foodie, only : integrand_object -use penf, only : R_P, I_P +use foodie_test_integrand_tester_object, only : integrand_tester_object +use penf, only : FR_P, R_P, I_P, str implicit none private public :: integrand_oscillation -type, extends(integrand_object) :: integrand_oscillation - !< The oscillation equations field. - !< - !< It is a FOODIE integrand class concrete extension. - !< - !<### Oscillation ODEs system - ! dU_dt !< Time derivative, residuals. - ! operators - procedure, pass(lhs), public :: local_error !<`||integrand_oscillation - integrand_oscillation||` operator. - ! + - procedure, pass(lhs), public :: integrand_add_integrand !< `+` operator. - procedure, pass(lhs), public :: integrand_add_real !< `+ real` operator. - procedure, pass(rhs), public :: real_add_integrand !< `real +` operator. - ! * - procedure, pass(lhs), public :: integrand_multiply_integrand !< `*` operator. - procedure, pass(lhs), public :: integrand_multiply_real !< `* real` operator. - procedure, pass(rhs), public :: real_multiply_integrand !< `real *` operator. - procedure, pass(lhs), public :: integrand_multiply_real_scalar !< `* real_scalar` operator. - procedure, pass(rhs), public :: real_scalar_multiply_integrand !< `real_scalar *` operator. - ! - - procedure, pass(lhs), public :: integrand_sub_integrand !< `-` operator. - procedure, pass(lhs), public :: integrand_sub_real !< `- real` operator. - procedure, pass(rhs), public :: real_sub_integrand !< `real -` operator. - ! = - procedure, pass(lhs), public :: assign_integrand !< `=` operator. - procedure, pass(lhs), public :: assign_real !< `= real` operator. - ! override fast operators - procedure, pass(self), public :: t_fast !< Time derivative, residuals, fast mode. - procedure, pass(opr), public :: integrand_add_integrand_fast !< `+` fast operator. - procedure, pass(opr), public :: integrand_multiply_integrand_fast !< `*` fast operator. - procedure, pass(opr), public :: integrand_multiply_real_scalar_fast !< `* real_scalar` fast operator. - procedure, pass(opr), public :: integrand_subtract_integrand_fast !< `-` fast operator. +type, extends(integrand_tester_object) :: integrand_oscillation + !< The oscillation equations field. + !< + !< It is a FOODIE integrand class concrete extension. + !< + !<### Oscillation ODEs system + ! dU_dt !< Time derivative, residuals. + ! operators + procedure, pass(lhs), public :: local_error !<`||integrand_oscillation - integrand_oscillation||` operator. + ! + + procedure, pass(lhs), public :: integrand_add_integrand !< `+` operator. + procedure, pass(lhs), public :: integrand_add_real !< `+ real` operator. + procedure, pass(rhs), public :: real_add_integrand !< `real +` operator. + ! * + procedure, pass(lhs), public :: integrand_multiply_integrand !< `*` operator. + procedure, pass(lhs), public :: integrand_multiply_real !< `* real` operator. + procedure, pass(rhs), public :: real_multiply_integrand !< `real *` operator. + procedure, pass(lhs), public :: integrand_multiply_real_scalar !< `* real_scalar` operator. + procedure, pass(rhs), public :: real_scalar_multiply_integrand !< `real_scalar *` operator. + ! - + procedure, pass(lhs), public :: integrand_sub_integrand !< `-` operator. + procedure, pass(lhs), public :: integrand_sub_real !< `- real` operator. + procedure, pass(rhs), public :: real_sub_integrand !< `real -` operator. + ! = + procedure, pass(lhs), public :: assign_integrand !< `=` operator. + procedure, pass(lhs), public :: assign_real !< `= real` operator. + ! override fast operators + procedure, pass(self), public :: t_fast !< Time derivative, residuals, fast mode. + procedure, pass(opr), public :: integrand_add_integrand_fast !< `+` fast operator. + procedure, pass(opr), public :: integrand_multiply_integrand_fast !< `*` fast operator. + procedure, pass(opr), public :: integrand_multiply_real_scalar_fast !< `* real_scalar` fast operator. + procedure, pass(opr), public :: integrand_subtract_integrand_fast !< `-` fast operator. endtype integrand_oscillation contains ! auxiliary methods - pure subroutine initialize(self, U0, frequency) - !< Initialize integrand. - class(integrand_oscillation), intent(inout) :: self !< Integrand. - real(R_P), intent(in) :: U0(1:2) !< Initial state. - real(R_P), intent(in) :: frequency !< Frequency of oscillation. + function amplitude_phase(self) result(ap) + !< Compute amplitude and phase of the oscillation. + class(integrand_oscillation), intent(in) :: self !< Advection field. + real(R_P) :: ap(1:2) !< Amplitude and phase. - self%U = U0 - self%f = frequency - self%U0 = U0 - endsubroutine initialize + ap(1) = sqrt(self%U(1)**2 + self%U(2)**2) + ap(2) = atan(-self%U(1) / self%U(2)) + endfunction amplitude_phase pure function exact_solution(self, t) result(exact) !< Return exact solution. @@ -103,6 +109,17 @@ pure function exact_solution(self, t) result(exact) exact(2) = self%U0(1) * sin(self%f * t) + self%U0(2) * cos(self%f * t) endfunction exact_solution + pure subroutine initialize(self, U0, frequency) + !< Initialize integrand. + class(integrand_oscillation), intent(inout) :: self !< Integrand. + real(R_P), intent(in) :: U0(1:2) !< Initial state. + real(R_P), intent(in) :: frequency !< Frequency of oscillation. + + self%U = U0 + self%f = frequency + self%U0 = U0 + endsubroutine initialize + pure function output(self) result(state) !< Extract integrand state field. class(integrand_oscillation), intent(in) :: self !< Integrand. @@ -111,11 +128,73 @@ pure function output(self) result(state) state = self%U endfunction output - ! deferred methods + ! integrand_tester_object deferred methods + pure function description(self, prefix) result(desc) + !< Return informative integrator description. + class(integrand_oscillation), intent(in) :: self !< Integrator. + character(*), intent(in), optional :: prefix !< Prefixing string. + character(len=:), allocatable :: desc !< Description. + character(len=:), allocatable :: prefix_ !< Prefixing string, local variable. + + prefix_ = '' ; if (present(prefix)) prefix_ = prefix + desc = prefix//'oscillation' + endfunction description + + subroutine export_tecplot(self, file_name, t, scheme, close_file) + !< Export integrand to Tecplot file. + class(integrand_oscillation), intent(in) :: self !< Advection field. + character(*), intent(in), optional :: file_name !< File name. + real(R_P), intent(in), optional :: t !< Time. + character(*), intent(in), optional :: scheme !< Scheme used to integrate integrand. + logical, intent(in), optional :: close_file !< Flag for closing file. + logical, save :: is_open=.false. !< Flag for checking if file is open. + integer(I_P), save :: file_unit !< File unit. + integer(I_P) :: i !< Counter. + + if (present(close_file)) then + if (close_file .and. is_open) then + close(unit=file_unit) + is_open = .false. + endif + else + if (present(file_name)) then + if (is_open) close(unit=file_unit) + open(newunit=file_unit, file=trim(adjustl(file_name))) + is_open = .true. + write(unit=file_unit, fmt='(A)') 'VARIABLES="t" "x" "y" "amplitude" "phase"' + endif + if (present(t) .and. present(scheme) .and. is_open) then + write(unit=file_unit, fmt='(A)') 'ZONE T="'//trim(adjustl(scheme))//'"' + write(unit=file_unit, fmt='(5('//FR_P//',1X))') t, self%U, self%amplitude_phase() + elseif (present(t) .and. is_open) then + write(unit=file_unit, fmt='(5('//FR_P//',1X))') t, self%U, self%amplitude_phase() + endif + endif + endsubroutine export_tecplot + + subroutine parse_cli(self, cli) + !< Initialize from command line interface. + class(integrand_oscillation), intent(inout) :: self !< Advection field. + type(command_line_interface), intent(inout) :: cli !< Command line interface handler. + + call cli%get(switch='-f', val=self%f, error=cli%error) ; if (cli%error/=0) stop + call cli%get(switch='-U0', val=self%U0, error=cli%error) ; if (cli%error/=0) stop + self%U = self%U0 + endsubroutine parse_cli + + subroutine set_cli(cli) + !< Set command line interface. + type(command_line_interface), intent(inout) :: cli !< Command line interface handler. + + 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') + endsubroutine set_cli + + ! integrand_object deferred methods pure function integrand_dimension(self) !< return integrand dimension. class(integrand_oscillation), intent(in) :: self !< integrand. - integer(i_p) :: integrand_dimension !< integrand dimension. + integer(I_P) :: integrand_dimension !< integrand dimension. integrand_dimension = size(self%U, dim=1) endfunction integrand_dimension @@ -270,9 +349,9 @@ pure subroutine assign_integrand(lhs, rhs) select type(rhs) class is(integrand_oscillation) - lhs%U = rhs%U lhs%f = rhs%f lhs%U0 = rhs%U0 + lhs%U = rhs%U endselect endsubroutine assign_integrand diff --git a/src/tests/tester/foodie_test_integrand_tester_object.f90 b/src/tests/tester/foodie_test_integrand_tester_object.f90 new file mode 100644 index 00000000..50d3bc54 --- /dev/null +++ b/src/tests/tester/foodie_test_integrand_tester_object.f90 @@ -0,0 +1,58 @@ +!< Define [[integrand_tester_object]], the abstract tester integrand. + +module foodie_test_integrand_tester_object +!< Define [[integrand_tester_object]], the abstract tester integrand. + +use flap, only : command_line_interface +use foodie, only : integrand_object +use penf, only : FR_P, R_P, I_P, str + +implicit none +private +public :: integrand_tester_object + +type, abstract, extends(integrand_object) :: integrand_tester_object + !< The abstract tester integrand. + !< + !< This abstract provided some auxiliary methods useful for the tester machinery. + contains + procedure(description_interface), pass(self), deferred :: description !< Return an informative description of the test. + procedure(export_tecplot_interface), pass(self), deferred :: export_tecplot !< Export integrand to Tecplot file. + procedure(parse_cli_interface), pass(self), deferred :: parse_cli !< Initialize from command line interface. + procedure(set_cli_interface), nopass, deferred :: set_cli !< Set command line interface. +endtype integrand_tester_object + +abstract interface + !< Abstract interfaces of [[integrand_tester_object]] class. + pure function description_interface(self, prefix) result(desc) + !< Return informative integrator description. + import :: integrand_tester_object + class(integrand_tester_object), intent(in) :: self !< Integrand. + character(*), intent(in), optional :: prefix !< Prefixing string. + character(len=:), allocatable :: desc !< Description. + endfunction description_interface + + subroutine export_tecplot_interface(self, file_name, t, scheme, close_file) + !< Export integrand to Tecplot file. + import :: integrand_tester_object, R_P + class(integrand_tester_object), intent(in) :: self !< Advection field. + character(*), intent(in), optional :: file_name !< File name. + real(R_P), intent(in), optional :: t !< Time. + character(*), intent(in), optional :: scheme !< Scheme used to integrate integrand. + logical, intent(in), optional :: close_file !< Flag for closing file. + endsubroutine export_tecplot_interface + + subroutine parse_cli_interface(self, cli) + !< Initialize from command line interface. + import :: command_line_interface, integrand_tester_object + class(integrand_tester_object), intent(inout) :: self !< Advection field. + type(command_line_interface), intent(inout) :: cli !< Command line interface handler. + endsubroutine parse_cli_interface + + subroutine set_cli_interface(cli) + !< Set command line interface. + import :: command_line_interface + type(command_line_interface), intent(inout) :: cli !< Command line interface handler. + endsubroutine set_cli_interface +endinterface +endmodule foodie_test_integrand_tester_object diff --git a/src/tests/tester/foodie_tester.f90 b/src/tests/tester/foodie_tester.f90 new file mode 100644 index 00000000..33733711 --- /dev/null +++ b/src/tests/tester/foodie_tester.f90 @@ -0,0 +1,352 @@ +!< Tester factory of FOODIE integrators. + +module foodie_test_object +!< Definition of [[test_object]] for FOODIE tester factory. + +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, & + integrand_object, & + 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_multistage_object, & + integrator_multistage_multistep_object, & + integrator_multistep_object, & + 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_ladvection, only : integrand_ladvection +use foodie_test_integrand_oscillation, only : integrand_oscillation +use foodie_test_integrand_tester_object, only : integrand_tester_object +use penf, only : I_P, R_P, FR_P, str, strz + +implicit none +private +public :: test_object + +type :: test_object + !< Generic FOODIE test object. + !< + !< 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 !< Error handler. + character(99) :: test !< Test executed. + character(99) :: scheme !< Scheme used. + real(R_P), allocatable :: Dt(:) !< Time step(s) exercised. + logical :: is_fast !< Flag for activating fast schemes. + integer(I_P) :: implicit_iterations !< Number of iterations (implicit solvers). + integer(I_P) :: stages !< Number of stages. + real(R_P) :: final_time !< Final integration time. + logical :: save_results !< Flag for activating results saving. + character(99) :: output !< Output files basename. + integer(I_P) :: save_frequency !< Save frequency. + logical :: verbose !< Flag for activating verbose output. + type(integrand_ladvection) :: ladvection_0 !< Initial conditions for linear advection test. + type(integrand_oscillation) :: oscillation_0 !< Initial conditions for oscillation test. + class(integrand_tester_object), allocatable :: integrand_0 !< Initial conditions. + 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. +endtype test_object + +contains + ! public methods + subroutine execute(self) + !< Execute test(s). + class(test_object), intent(inout) :: self !< Test. + character(99), allocatable :: integrator_schemes(:) !< Name of FOODIE integrator schemes. + integer(I_P) :: s !< Counter. + integer(I_P) :: t !< 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) + do t=1, size(self%Dt) + call integrate(scheme=trim(integrator_schemes(s)), & + integrand_0=self%integrand_0, & + Dt=self%Dt(t), & + final_time=self%final_time, & + iterations=self%implicit_iterations, & + stages=self%stages, & + is_fast=self%is_fast, & + save_results=self%save_results, & + output_base_name=trim(adjustl(self%output)), & + save_frequency=self%save_frequency) + enddo + enddo + endsubroutine execute + + ! private methods + subroutine initialize(self) + !< Initialize test: set Command Line Interface, parse it and check its validity. + class(test_object), 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_tester', & + authors = 'Fortran-FOSS-Programmers', & + license = 'GNU GPLv3', & + description = 'Tester factory of FOODIE integrators', & + examples = ["foodie_tester --scheme euler_explicit --save_results ", & + "foodie_tester --scheme all -r "]) + call cli%add(switch='--test', switch_ab='-t', help='test executed', required=.false., def='oscillation', & + act='store', choices='linear_advection,oscillation') + call cli%add(switch='--scheme', switch_ab='-s', help='integrator scheme used', required=.false., def='all', 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='--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='--final_time', switch_ab='-ft', help='integration time', required=.false., def='1e6', 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') + call cli%add(switch='--save_frequency', help='save frequency', required=.false., act='store', def='1') + call cli%add(switch='--verbose', help='Verbose output', required=.false., act='store_true', def='.false.') + endassociate + call self%ladvection_0%set_cli(cli=self%cli) + call self%oscillation_0%set_cli(cli=self%cli) + 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. + real(R_P), allocatable :: initial_state(:) !< Initial integrand state. + integer(I_P) :: i !< Counter. + + call self%cli%parse(error=self%error) + call self%cli%get(switch='-t', val=self%test, error=self%error) ; if (self%error/=0) stop + call self%cli%get(switch='-s', val=self%scheme, 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='--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(switch='-ft', val=self%final_time, error=self%error) ; if (self%error/=0) stop + call self%cli%get(switch='-r', val=self%save_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='--save_frequency', val=self%save_frequency, error=self%error) ; if (self%error/=0) stop + call self%cli%get(switch='--verbose', val=self%verbose, error=self%error) ; if (self%error/=0) stop + call self%ladvection_0%parse_cli(cli=self%cli) + call self%oscillation_0%parse_cli(cli=self%cli) + + select case(trim(adjustl(self%test))) + case('linear_advection') + allocate(integrand_ladvection :: self%integrand_0) + self%integrand_0 = self%ladvection_0 + case('oscillation') + allocate(integrand_oscillation :: self%integrand_0) + self%integrand_0 = self%oscillation_0 + endselect + + if (.not.is_dt_valid()) then + write(stderr, '(A)') 'error: the final integration time must be an exact multiple of the time step used' + write(stderr, '(A)') ' and time step must respect CFL condition, if any' + write(stderr, '(A)') 'Final integration time: '//str(self%final_time, .true.) + write(stderr, '(A)') 'Time step: '//str(self%Dt, .true.) + stop + endif + + 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() + write(stderr, '(A)') 'error: the scheme "'//trim(adjustl(self%scheme))//'" is unknown!' + write(stderr, '(A)') 'Supported classes of schemes are:' + do i=1, size(integrator_class_names, dim=1) + write(stderr, '(A)') ' '//trim(integrator_class_names(i)) + enddo + write(stderr, '(A)') 'Supported schemes are:' + do i=1, size(integrator_schemes, dim=1) + write(stderr, '(A)') ' '//trim(integrator_schemes(i)) + enddo + stop + endif + 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 (is_dt_valid) then + select type(integrand=>self%integrand_0) + type is(integrand_ladvection) + is_dt_valid = is_dt_valid .and. self%Dt(t) <= integrand%Dt(final_time=self%final_time) + if (.not.is_dt_valid) then + write(stderr, '(A)') 'error: Dt violates CFL condition, Dt_max = '//str(integrand%Dt(final_time=self%final_time)) + endif + endselect + endif + if (.not.is_dt_valid) exit + enddo + endfunction is_dt_valid + endsubroutine initialize + + ! 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, integrand_0, Dt, final_time, iterations, stages, is_fast, save_results, output_base_name, & + save_frequency) + !< Integrate integrand by means of the given scheme. + character(*), intent(in) :: scheme !< Selected scheme. + class(integrand_tester_object), intent(in) :: integrand_0 !< Initial conditions. + real(R_P), intent(in) :: Dt !< Time step. + real(R_P), intent(in) :: final_time !< Final integration time. + 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. + logical, intent(in) :: save_results !< Save results. + character(*), intent(in) :: output_base_name !< Base name of output results file. + integer(I_P), intent(in) :: save_frequency !< Save frequency. + class(integrator_object), allocatable :: integrator !< The integrator. + type(integrator_runge_kutta_ssp) :: integrator_start !< The (auto) start integrator. + class(integrand_tester_object), allocatable :: integrand !< Integrand. + real(R_P) :: time !< Time. + integer(I_P) :: step !< Time steps counter. + + allocate(integrand, mold=integrand_0) ; integrand = integrand_0 + + call foodie_integrator_factory(scheme=scheme, integrator=integrator, stages=stages, & + tolerance=1e2_R_P, iterations=iterations, autoupdate=.true., U=integrand_0) + if (is_fast) call check_scheme_has_fast_mode(scheme=trim(adjustl(scheme)), integrator=integrator) + + if (integrator%is_multistep()) call integrator_start%initialize(scheme='runge_kutta_ssp_stages_5_order_4', U=integrand_0) + + step = 0 + time = 0._R_P + if (save_results) call integrand%export_tecplot(file_name=output_base_name// & + integrand%description(prefix='-')// & + integrator%description(prefix='-')// & + '-steps_'//trim(strz(int(final_time/Dt), 10))//'.dat', & + t=time, scheme=scheme) + + select type(integrator) + class is(integrator_multistage_object) + do + step = step + 1 + if (is_fast) then + call integrator%integrate_fast(U=integrand, Dt=Dt, t=time) + else + call integrator%integrate(U=integrand, Dt=Dt, t=time) + endif + time = time + Dt + if ((time >= final_time)) exit + call integrand_export_tecplot + enddo + + class is(integrator_multistep_object) + do + step = step + 1 + if (integrator%steps_number() >= step) then + call integrator_start%integrate(U=integrand, Dt=Dt, t=time) + integrator%previous(step) = integrand + time = time + Dt + integrator%Dt(step) = Dt + integrator%t(step) = time + else + if (is_fast) then + call integrator%integrate_fast(U=integrand, Dt=Dt, t=time) + else + call integrator%integrate(U=integrand, Dt=Dt, t=time) + endif + time = time + Dt + endif + if ((time >= final_time)) exit + call integrand_export_tecplot + enddo + + class is(integrator_multistage_multistep_object) + do + step = step + 1 + if (integrator%steps_number() >= step) then + call integrator_start%integrate(U=integrand, Dt=Dt, t=time) + integrator%previous(step) = integrand + time = time + Dt + integrator%Dt(step) = Dt + integrator%t(step) = time + else + if (is_fast) then + call integrator%integrate_fast(U=integrand, Dt=Dt,t=time) + else + call integrator%integrate(U=integrand, Dt=Dt, t=time) + endif + time = time + Dt + endif + if ((time >= final_time)) exit + call integrand_export_tecplot + enddo + endselect + + select type(integrand) + type is(integrand_ladvection) + if (save_results .and. mod(step, save_frequency)==0) call integrand%export_tecplot(t=time, scheme=scheme) + type is(integrand_oscillation) + if (save_results .and. mod(step, save_frequency)==0) call integrand%export_tecplot(t=time) + endselect + if (save_results) call integrand%export_tecplot(close_file=.true.) + contains + subroutine integrand_export_tecplot + !< Export current integrand solution to tecplot file. + + select type(integrand) + type is(integrand_ladvection) + if (save_results .and. mod(step, save_frequency)==0) call integrand%export_tecplot(t=time, scheme=scheme) + type is(integrand_oscillation) + if (save_results .and. mod(step, save_frequency)==0) call integrand%export_tecplot(t=time) + endselect + endsubroutine integrand_export_tecplot + endsubroutine integrate +endmodule foodie_test_object + +program foodie_tester +!< Tester factory of FOODIE integrators. + +use foodie_test_object, only : test_object + +implicit none +type(test_object) :: test !< FOODIE test. + +call test%execute +endprogram foodie_tester diff --git a/src/third_party/WenOOF b/src/third_party/WenOOF index f4f5a40e..664cb5b1 160000 --- a/src/third_party/WenOOF +++ b/src/third_party/WenOOF @@ -1 +1 @@ -Subproject commit f4f5a40e4f4839a03559c1142e92701308331395 +Subproject commit 664cb5b131609a915253f64d041c0863f33ee8ee