!> A small helper module for storing and providing RK schemes module rk_schemes implicit none private public :: rk_scheme_type, get_rk_scheme, get_rk_scheme_by_id public :: get_rk_schemes_as_text_options public :: rk_dopri, rk_f45, rk_cashkarp public :: rk_heun, rk_ralston, rk_midpoint public :: rk_bs23, rk_bs45, rk_euler !> Internal named type to represent scheme ids type rk_scheme_id_type private integer :: id character(len=30) :: name end type rk_scheme_id_type !> Public type storing the data for a particular RK scheme type :: rk_scheme_type integer :: number_of_stages integer :: order character(len=30) :: name real, dimension(:), allocatable :: lower_order_coeffs real, dimension(:), allocatable :: high_order_coeffs real, dimension(:), allocatable :: time_points real, dimension(:, :), allocatable :: coeffs logical :: follow_high_order contains procedure :: validate => rk_scheme_validate end type rk_scheme_type !> Dormand-Prince 45 method type(rk_scheme_id_type), parameter :: rk_dopri = rk_scheme_id_type(id=1, name='dopri') !> Runge-Kutta-Fehlberg 45 method type(rk_scheme_id_type), parameter :: rk_f45 = rk_scheme_id_type(id=2, name='rkf45') !> Cash-Karp 45 method type(rk_scheme_id_type), parameter :: rk_cashkarp = rk_scheme_id_type(id=3, name='cashkarp') !> Heun-Euler 12 method type(rk_scheme_id_type), parameter :: rk_heun = rk_scheme_id_type(id=4, name='heun') !> Ralston-Euler 12 method type(rk_scheme_id_type), parameter :: rk_ralston = rk_scheme_id_type(id=5, name='ralston') !> Midpoint-Euler 12 method type(rk_scheme_id_type), parameter :: rk_midpoint = rk_scheme_id_type(id=6, name='midpoint') !> Bogacki-Shampine 23 method type(rk_scheme_id_type), parameter :: rk_bs23 = rk_scheme_id_type(id=7, name='bs23') !> Bogacki-Shampine 45 method type(rk_scheme_id_type), parameter :: rk_bs45 = rk_scheme_id_type(id=8, name='bs45') !> Single step Euler type(rk_scheme_id_type), parameter :: rk_euler = rk_scheme_id_type(id=9, name='euler') contains !> Returns an array of text_option values representing the known rk schemes, mapping !> names to integer ids pure function get_rk_schemes_as_text_options() result(options) use text_options, only: text_option implicit none type(text_option), dimension(:), allocatable :: options options = [ & text_option(rk_dopri%name, rk_dopri%id), & text_option(rk_f45%name, rk_f45%id), & text_option(rk_cashkarp%name, rk_cashkarp%id), & text_option(rk_heun%name, rk_heun%id), & text_option(rk_ralston%name, rk_ralston%id), & text_option(rk_midpoint%name, rk_midpoint%id), & text_option(rk_bs23%name, rk_bs23%id), & text_option(rk_bs45%name, rk_bs45%id), & text_option(rk_euler%name, rk_euler%id), & text_option('default', rk_heun%id) & ] end function get_rk_schemes_as_text_options !> Returns an rk_scheme instance describing the scheme with id matching !> the passed id value. type(rk_scheme_type) function get_rk_scheme_by_id(id) result(scheme) use mp, only: mp_abort implicit none integer, intent(in) :: id select case(id) case(rk_dopri%id) scheme = get_rk_scheme(rk_dopri) case(rk_f45%id) scheme = get_rk_scheme(rk_f45) case(rk_cashkarp%id) scheme = get_rk_scheme(rk_cashkarp) case(rk_heun%id) scheme = get_rk_scheme(rk_heun) case(rk_ralston%id) scheme = get_rk_scheme(rk_ralston) case(rk_midpoint%id) scheme = get_rk_scheme(rk_midpoint) case(rk_bs23%id) scheme = get_rk_scheme(rk_bs23) case(rk_bs45%id) scheme = get_rk_scheme(rk_bs45) case(rk_euler%id) scheme = get_rk_scheme(rk_euler) case default call mp_abort("Invalid RK scheme id passed to get_rk_scheme_by_id", .true.) end select end function get_rk_scheme_by_id !> Returns an rk_scheme instance describing the scheme represented by !> the passed variant type. type(rk_scheme_type) function get_rk_scheme(variant) result(scheme) use mp, only: mp_abort, proc0 implicit none type(rk_scheme_id_type), intent(in) :: variant integer :: validity scheme%name = variant%name select case(variant%id) case(rk_dopri%id) scheme%number_of_stages = 7 scheme%order = 5 allocate(scheme%lower_order_coeffs, source = & [5179/57600.0, 0.0,7571/16695.0, 393/640.0, -92097/339200.0, 187/2100.0, 1/40.0]) scheme%high_order_coeffs = [35/384.0, 0.0, 500/1113.0, 125/192.0, -2187/6784.0, 11/84.0, 0.0] scheme%time_points = [0.0, 1/5.0, 3/10.0, 4/5.0, 8/9.0, 1.0, 1.0] scheme%follow_high_order = .true. allocate(scheme%coeffs(scheme%number_of_stages - 1, scheme%number_of_stages)) scheme%coeffs = 0.0 scheme%coeffs(:1, 2) = [1/5.0] scheme%coeffs(:2, 3) = [3/40.0, 9/40.0] scheme%coeffs(:3, 4) = [44/45.0, -56/15.0, 32/9.0] scheme%coeffs(:4, 5) = [19372/6561.0, -25360/2187.0, 64448/6561.0, -212/729.0] scheme%coeffs(:5, 6) = [9017/3168.0, -355/33.0, 46732/5247.0, 49/176.0, -5103/18656.0] scheme%coeffs(:6, 7) = [35/384.0, 0.0, 500/1113.0, 125/192.0, -2187/6784.0, 11/84.0] case(rk_f45%id) scheme%number_of_stages = 6 scheme%order = 5 allocate(scheme%lower_order_coeffs, source = & [25/216.0, 0.0, 1408/2565.0, 2197/4104.0, -1/5.0, 0.0]) scheme%high_order_coeffs = [16/135.0, 0.0, 6656/12825.0, 28561/56430.0, -9/50.0, 2/55.0] scheme%time_points = [0.0, 1/4.0, 3/8.0, 12/13.0, 1.0, 1/2.0] scheme%follow_high_order = .true. allocate(scheme%coeffs(scheme%number_of_stages - 1, scheme%number_of_stages)) scheme%coeffs = 0.0 scheme%coeffs(:1, 2) = [1/4.0] scheme%coeffs(:2, 3) = [3/32.0, 9/32.0] scheme%coeffs(:3, 4) = [1932/2197.0, -7200/2197.0, 7296/2197.0] scheme%coeffs(:4, 5) = [439/216.0, -8.0, 3680/513.0, -845/4104.0] scheme%coeffs(:5, 6) = [-8/27.0, 2.0, -3544/2565.0, 1859/4104.0, -11/40.0] case(rk_cashkarp%id) scheme%number_of_stages = 6 scheme%order = 5 allocate(scheme%lower_order_coeffs, source = & [2825/27648.0, 0.0, 18575/48384.0, 13525/55296.0, 277/14336.0, 1/4.0]) scheme%high_order_coeffs = [37/378.0, 0.0, 250/621.0, 125/594.0, 0.0, 512/1771.0] scheme%time_points = [0.0, 1/5.0, 3/10.0, 3/5.0, 1.0, 7/8.0] scheme%follow_high_order = .true. allocate(scheme%coeffs(scheme%number_of_stages - 1, scheme%number_of_stages)) scheme%coeffs = 0.0 scheme%coeffs(:1, 2) = [1/5.0] scheme%coeffs(:2, 3) = [3/40.0, 9/40.0] scheme%coeffs(:3, 4) = [3/10.0, -9/10.0, 6/5.0] scheme%coeffs(:4, 5) = [-11/54.0, 5/2.0, -70/27.0, 35/27.0] scheme%coeffs(:5, 6) = [1631/55296.0, 175/512.0, 575/13824.0, 44275/110592.0, 253/4096.0] case(rk_heun%id) scheme%number_of_stages = 2 scheme%order = 2 allocate(scheme%lower_order_coeffs, source = & [1.0, 0.0]) scheme%high_order_coeffs = [1/2.0, 1/2.0] scheme%time_points = [0.0, 1.0] scheme%follow_high_order = .true. allocate(scheme%coeffs(scheme%number_of_stages - 1, scheme%number_of_stages)) scheme%coeffs = 0.0 scheme%coeffs(:1, 2) = [1.0] case(rk_ralston%id) scheme%number_of_stages = 2 scheme%order = 2 allocate(scheme%lower_order_coeffs, source = & [1.0, 0.0]) scheme%high_order_coeffs = [1/4.0, 3/4.0] scheme%time_points = [0.0, 2/3.0] scheme%follow_high_order = .true. allocate(scheme%coeffs(scheme%number_of_stages - 1, scheme%number_of_stages)) scheme%coeffs = 0.0 scheme%coeffs(:1, 2) = [2/3.0] case(rk_midpoint%id) scheme%number_of_stages = 2 scheme%order = 2 allocate(scheme%lower_order_coeffs, source = & [1.0, 0.0]) scheme%high_order_coeffs = [0.0, 1.0] scheme%time_points = [0.0, 1/2.0] scheme%follow_high_order = .true. allocate(scheme%coeffs(scheme%number_of_stages - 1, scheme%number_of_stages)) scheme%coeffs = 0.0 scheme%coeffs(:1, 2) = [1/2.0] case(rk_bs23%id) scheme%number_of_stages = 4 scheme%order = 3 allocate(scheme%lower_order_coeffs, source = & [7/24.0, 1/4.0, 1/3.0, 1/8.0]) scheme%high_order_coeffs = [2/9.0, 1/3.0, 4/9.0, 0.0] scheme%time_points = [0.0, 1/2.0, 3/4.0, 1.0] scheme%follow_high_order = .true. allocate(scheme%coeffs(scheme%number_of_stages - 1, scheme%number_of_stages)) scheme%coeffs = 0.0 scheme%coeffs(:1, 2) = [1/2.0] scheme%coeffs(:2, 3) = [0.0, 3/4.0] scheme%coeffs(:3, 4) = [2/9.0, 1/3.0, 4/9.0] case(rk_bs45%id) scheme%number_of_stages = 8 scheme%order = 5 allocate(scheme%lower_order_coeffs, source = & [6059/80640.0, 0.0, 8559189/30983680.0, 26411/124800.0, & -927/89600.0, 443/1197.0, 7267/94080.0, 0.0]) scheme%high_order_coeffs = [587/8064.0, 0.0, 4440339/15491840.0, 24353/124800.0, & 387/44800.0, 2152/5985.0, 7267/94080.0, 0.0] scheme%time_points = [0.0, 1/6.0, 2/9.0, 3/7.0, 2/3.0, 3/4.0, 1.0, 1.0] scheme%follow_high_order = .true. allocate(scheme%coeffs(scheme%number_of_stages - 1, scheme%number_of_stages)) scheme%coeffs = 0.0 scheme%coeffs(:1, 2) = [1/6.0] scheme%coeffs(:2, 3) = [2/27.0, 4/27.0] scheme%coeffs(:3, 4) = [183/1372.0, -162/343.0, 1053/1372.0] scheme%coeffs(:4, 5) = [68/297.0, -4/11.0, 42/143.0, 1960/3861.0] scheme%coeffs(:5, 6) = [597/22528.0, 81/352.0, 63099/585728.0, 58653/366080.0, & 4617/20480.0] scheme%coeffs(:6, 7) = [174197/959244.0, -30942/79937.0, 8152137/19744439.0, & 666106/1039181.0, -29421/29068.0, 482048/414219.0] scheme%coeffs(:7, 8) = [587/8064.0, 0.0, 4440339/15491840.0, 24353/124800.0, & 387/44800.0, 2152/5985.0, 7267/94080.0] case(rk_euler%id) scheme%number_of_stages = 1 scheme%order = 1 allocate(scheme%lower_order_coeffs, source = & [1.0]) scheme%high_order_coeffs = [1.0] scheme%time_points = [0.0] scheme%follow_high_order = .true. allocate(scheme%coeffs(scheme%number_of_stages - 1, scheme%number_of_stages)) scheme%coeffs = 0.0 case default call mp_abort("Invalid RK scheme passed to get_rk_scheme", .true.) end select validity = scheme%validate() if (proc0 .and. (validity /= 0)) then print*,'The RK scheme failed validity check with error code:',validity,'and scheme',scheme%name call mp_abort("RK scheme failed validity check", .true.) end if end function get_rk_scheme !> Perform a small check of the consistency. Returns an error !> code built from bitwise flags to give information about which !> checks, if any, failed. Success is repesented by zero. integer pure function rk_scheme_validate(self) result(valid) implicit none class(rk_scheme_type), intent(in) :: self integer :: i real, parameter :: tolerance = 10*epsilon(1.0) valid = 0 if (.not.(abs(sum(self%lower_order_coeffs) - 1.0) < tolerance)) valid = valid + 1 if (.not.(abs(sum(self%high_order_coeffs) - 1.0) < tolerance)) valid = valid + 2 do i = 1, self%number_of_stages if (.not.(abs(sum(self%coeffs(:, i)) - self%time_points(i)) < tolerance)) valid = valid + (2**(i+1)) end do end function rk_scheme_validate end module rk_schemes