Report Typos and Errors    
Semi-Lagrangian Library
Modular library for kinetic and gyrokinetic simulations of plasmas in fusion energy devices.
sll_m_rk_explicit.F90
Go to the documentation of this file.
1 !----------------------
2 ! A note about notation
3 !----------------------
4 ! rkXY:
5 ! e = explicit
6 ! i = implicit (fully)
7 ! d = diagonally implicit
8 ! m = embedded
9 ! l = low-storage
10 !----------------------
11 
13 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
14 #include "sll_assert.h"
15 
16  use sll_m_working_precision, only: &
17  f64
18 
19  use sll_m_ode_integrator_base, only: &
20  sll_c_ode, &
22 
23  use sll_m_vector_space_base, only: &
25 
26  implicit none
27 
28  public :: &
35 
36  private
37 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
38 
40  integer, parameter :: wp = f64
41 
42  !----------------------------------------------------------------------------
43  ! Explicit Euler method
44  !----------------------------------------------------------------------------
46 
47  contains
48  procedure :: init => init__rk1e_fwd_euler
49  procedure :: step => step__rk1e_fwd_euler
50  procedure :: clean => clean__rk1e_fwd_euler
51 
52  end type sll_t_rk1e_fwd_euler
53 
54  !----------------------------------------------------------------------------
55  ! Explicit midpoint method
56  !----------------------------------------------------------------------------
58 
59  contains
60  procedure :: init => init__rk2e_midpoint
61  procedure :: step => step__rk2e_midpoint
62  procedure :: clean => clean__rk2e_midpoint
63 
64  end type sll_t_rk2e_midpoint
65 
66  !----------------------------------------------------------------------------
67  ! Explicit trapezoidal rule (Heun's method)
68  !----------------------------------------------------------------------------
70 
71  contains
72  procedure :: init => init__rk2e_heun
73  procedure :: step => step__rk2e_heun
74  procedure :: clean => clean__rk2e_heun
75 
76  end type sll_t_rk2e_heun
77 
78  !----------------------------------------------------------------------------
79  ! Ralston's method
80  !----------------------------------------------------------------------------
82 
83  contains
84  procedure :: init => init__rk2e_ralston
85  procedure :: step => step__rk2e_ralston
86  procedure :: clean => clean__rk2e_ralston
87 
88  end type sll_t_rk2e_ralston
89 
90  !----------------------------------------------------------------------------
91  ! 3rd-order method by Heun
92  !----------------------------------------------------------------------------
94 
95  contains
96  procedure :: init => init__rk3e_heun3
97  procedure :: step => step__rk3e_heun3
98  procedure :: clean => clean__rk3e_heun3
99 
100  end type sll_t_rk3e_heun3
101 
102  !----------------------------------------------------------------------------
103  ! The 'classic' 4th-order RK
104  !----------------------------------------------------------------------------
106 
107  contains
108  procedure :: init => init__rk4e_classic
109  procedure :: step => step__rk4e_classic
110  procedure :: clean => clean__rk4e_classic
111 
112  end type sll_t_rk4e_classic
113 
114 !==============================================================================
115 contains
116 !==============================================================================
117 
118  !----------------------------------------------------------------------------
119  ! Explicit Euler method
120  !----------------------------------------------------------------------------
121  subroutine init__rk1e_fwd_euler(self, ode, t0, y0)
122  class(sll_t_rk1e_fwd_euler), intent(out) :: self
123  class(sll_c_ode), pointer, intent(in) :: ode
124  real(wp), intent(in) :: t0
125  class(sll_c_vector_space), intent(inout) :: y0
126 
127  self%ode => ode
128  sll_assert(t0 >= 0.0)
129  sll_assert(storage_size(y0) > 0)
130 
131  end subroutine init__rk1e_fwd_euler
132 
133  !----------------------------------------------------------------------------
134  subroutine step__rk1e_fwd_euler(self, t, y, h, ynew)
135  class(sll_t_rk1e_fwd_euler), intent(inout) :: self
136  real(wp), intent(in) :: t
137  class(sll_c_vector_space), intent(in) :: y
138  real(wp), intent(in) :: h
139  class(sll_c_vector_space), intent(inout) :: ynew
140 
141  ! ynew = y + h * f(t,y)
142  call self%ode%rhs(t, y, ynew) ! ynew = f(t,y)
143  call ynew%scal(h) ! ynew *= h
144  call ynew%incr(y) ! ynew += y
145 
146  end subroutine step__rk1e_fwd_euler
147 
148  !----------------------------------------------------------------------------
149  subroutine clean__rk1e_fwd_euler(self)
150  class(sll_t_rk1e_fwd_euler), intent(inout) :: self
151  ! Do nothing, because method does not require local storage
152  sll_assert(storage_size(self) > 0)
153  end subroutine clean__rk1e_fwd_euler
154 
155  !----------------------------------------------------------------------------
156  ! Explicit midpoint method
157  !----------------------------------------------------------------------------
158  subroutine init__rk2e_midpoint(self, ode, t0, y0)
159  class(sll_t_rk2e_midpoint), intent(out) :: self
160  class(sll_c_ode), pointer, intent(in) :: ode
161  real(wp), intent(in) :: t0
162  class(sll_c_vector_space), intent(inout) :: y0
163 
164  self%ode => ode ! Store pointer to ODE system
165  call y0%source(self%work, 1) ! Allocate temporary storage
166  sll_assert(t0 >= 0.0)
167 
168  end subroutine init__rk2e_midpoint
169 
170  !----------------------------------------------------------------------------
171  subroutine step__rk2e_midpoint(self, t, y, h, ynew)
172  class(sll_t_rk2e_midpoint), intent(inout) :: self
173  real(wp), intent(in) :: t
174  class(sll_c_vector_space), intent(in) :: y
175  real(wp), intent(in) :: h
176  class(sll_c_vector_space), intent(inout) :: ynew
177 
178  ! Half time step
179  real(wp) :: h2
180  h2 = h*0.5_f64
181 
182  ! yi = y + h/2 * f(t ,y )
183  ! ynew = y + h * f(t+h/2,yi)
184 
185  ! Compute 1st stage derivative: k1 = f(t,y)
186  call self%ode%rhs(t, y, self%work(1))
187 
188  ! Compute 2nd stage solution: y2 = y + h/2 * k1
189  call ynew%mult_add(h2, self%work(1), y) ! (use ynew as temporary)
190 
191  ! Compute 2nd stage derivative: k2 = f(t+h/2,y2)
192  call self%ode%rhs(t + h2, ynew, self%work(1)) ! (overwrite k1)
193 
194  ! Compute new solution: ynew = y + h * k2
195  call ynew%mult_add(h, self%work(1), y)
196 
197  end subroutine step__rk2e_midpoint
198 
199  !----------------------------------------------------------------------------
200  subroutine clean__rk2e_midpoint(self)
201  class(sll_t_rk2e_midpoint), intent(inout) :: self
202  deallocate (self%work)
203  end subroutine clean__rk2e_midpoint
204 
205  !----------------------------------------------------------------------------
206  ! Explicit trapezoidal rule (Heun's method)
207  !----------------------------------------------------------------------------
208  subroutine init__rk2e_heun(self, ode, t0, y0)
209  class(sll_t_rk2e_heun), intent(out) :: self
210  class(sll_c_ode), pointer, intent(in) :: ode
211  real(wp), intent(in) :: t0
212  class(sll_c_vector_space), intent(inout) :: y0
213 
214  self%ode => ode ! Store pointer to ODE system
215  call y0%source(self%work, 2) ! Allocate temporary storage
216  sll_assert(t0 >= 0.0)
217 
218  end subroutine init__rk2e_heun
219 
220  !----------------------------------------------------------------------------
221  subroutine step__rk2e_heun(self, t, y, h, ynew)
222  class(sll_t_rk2e_heun), intent(inout) :: self
223  real(wp), intent(in) :: t
224  class(sll_c_vector_space), intent(in) :: y
225  real(wp), intent(in) :: h
226  class(sll_c_vector_space), intent(inout) :: ynew
227 
228  ! Compute 1st stage derivative: k1 = f(t,y)
229  call self%ode%rhs(t, y, self%work(1))
230 
231  ! Compute 2nd stage solution: y2 = y + h * k1
232  call ynew%mult_add(h, self%work(1), y) ! (use ynew as temporary)
233 
234  ! Compute 2nd stage derivative: k2 = f(t+h,y2)
235  call self%ode%rhs(t + h, ynew, self%work(2))
236 
237  ! Compute average time derivative, multiplied by 2:
238  ! 2*f_avg = k1+k2
239  call self%work(1)%incr(self%work(2)) ! (overwrite k1)
240 
241  ! Compute new solution: ynew = y + h/2 * (k1+k2)
242  call ynew%mult_add(h*0.5_f64, self%work(1), y)
243 
244  end subroutine step__rk2e_heun
245 
246  !----------------------------------------------------------------------------
247  subroutine clean__rk2e_heun(self)
248  class(sll_t_rk2e_heun), intent(inout) :: self
249  deallocate (self%work)
250  end subroutine clean__rk2e_heun
251 
252  !----------------------------------------------------------------------------
253  ! Ralston's method
254  !----------------------------------------------------------------------------
255  subroutine init__rk2e_ralston(self, ode, t0, y0)
256  class(sll_t_rk2e_ralston), intent(out) :: self
257  class(sll_c_ode), pointer, intent(in) :: ode
258  real(wp), intent(in) :: t0
259  class(sll_c_vector_space), intent(inout) :: y0
260 
261  self%ode => ode ! Store pointer to ODE system
262  call y0%source(self%work, 2) ! Allocate temporary storage
263 
264  sll_assert(t0 >= 0.0)
265  end subroutine init__rk2e_ralston
266 
267  !----------------------------------------------------------------------------
268  subroutine step__rk2e_ralston(self, t, y, h, ynew)
269  class(sll_t_rk2e_ralston), intent(inout) :: self
270  real(wp), intent(in) :: t
271  class(sll_c_vector_space), intent(in) :: y
272  real(wp), intent(in) :: h
273  class(sll_c_vector_space), intent(inout) :: ynew
274 
275  real(wp), parameter :: two_thirds = 2.0_f64/3.0_f64
276 
277  ! Compute 1st stage derivative: k1 = f(t,y)
278  call self%ode%rhs(t, y, self%work(1))
279 
280  ! Compute 2nd stage solution: y2 = y + 2/3*h * k1
281  call ynew%mult_add(h*two_thirds, self%work(1), y) ! (use ynew as temp.)
282 
283  ! Compute 2nd stage derivative: k2 = f(t+2/3*h,y2)
284  call self%ode%rhs(t + h*two_thirds, ynew, self%work(2))
285 
286  ! Compute average time derivative, multiplied by 4:
287  ! 4*f_avg = k1+3*k2
288  call self%work(1)%incr_mult(3.0_f64, self%work(2)) ! (overwrite k1)
289 
290  ! Compute new solution: ynew = y + h/4 * (k1+3*k2)
291  call ynew%mult_add(h*0.25_f64, self%work(1), y)
292 
293  end subroutine step__rk2e_ralston
294 
295  !----------------------------------------------------------------------------
296  subroutine clean__rk2e_ralston(self)
297  class(sll_t_rk2e_ralston), intent(inout) :: self
298  deallocate (self%work)
299  end subroutine clean__rk2e_ralston
300 
301  !----------------------------------------------------------------------------
302  ! 3rd-order method by Heun
303  !----------------------------------------------------------------------------
304  subroutine init__rk3e_heun3(self, ode, t0, y0)
305  class(sll_t_rk3e_heun3), intent(out) :: self
306  class(sll_c_ode), pointer, intent(in) :: ode
307  real(wp), intent(in) :: t0
308  class(sll_c_vector_space), intent(inout) :: y0
309 
310  self%ode => ode ! Store pointer to ODE system
311  call y0%source(self%work, 2) ! Allocate temporary storage
312 
313  sll_assert(t0 >= 0.0)
314 
315  end subroutine init__rk3e_heun3
316 
317  !----------------------------------------------------------------------------
318  subroutine step__rk3e_heun3(self, t, y, h, ynew)
319  class(sll_t_rk3e_heun3), intent(inout) :: self
320  real(wp), intent(in) :: t
321  class(sll_c_vector_space), intent(in) :: y
322  real(wp), intent(in) :: h
323  class(sll_c_vector_space), intent(inout) :: ynew
324 
325  ! Compute 1st stage derivative: k1 = f(t,y)
326  call self%ode%rhs(t, y, self%work(1))
327 
328  ! Compute 2nd stage solution: y2 = y + h/3 * k1
329  call ynew%mult_add(h/3.0_f64, self%work(1), y) ! (use ynew as temporary)
330 
331  ! Compute 2nd stage derivative: k2 = f(t+h/3,y2)
332  call self%ode%rhs(t + h/3.0_f64, ynew, self%work(2))
333 
334  ! Compute 3rd stage solution: y3 = y + 2/3*h *k2
335  call ynew%mult_add(h/1.5_f64, self%work(2), y) ! (use ynew as temporary)
336 
337  ! Compute 3rd stage derivative: k3 = f(t+2/3*h,y3)
338  call self%ode%rhs(t + h/1.5_f64, ynew, self%work(2)) ! (overwrite k2)
339 
340  ! Compute average time derivative, multiplied by 4:
341  ! 4*f_avg = k1+3*k3
342  call self%work(1)%incr_mult(3.0_f64, self%work(2)) ! (overwrite k1)
343 
344  ! Compute new solution: ynew = y + h/4 * (k1+3*k2)
345  call ynew%mult_add(h*0.25_f64, self%work(1), y)
346 
347  end subroutine step__rk3e_heun3
348 
349  !----------------------------------------------------------------------------
350  subroutine clean__rk3e_heun3(self)
351  class(sll_t_rk3e_heun3), intent(inout) :: self
352  deallocate (self%work)
353  end subroutine clean__rk3e_heun3
354 
355  !----------------------------------------------------------------------------
356  ! The 'classic' 4th-order RK
357  !----------------------------------------------------------------------------
358  subroutine init__rk4e_classic(self, ode, t0, y0)
359  class(sll_t_rk4e_classic), intent(out) :: self
360  class(sll_c_ode), pointer, intent(in) :: ode
361  real(wp), intent(in) :: t0
362  class(sll_c_vector_space), intent(inout) :: y0
363 
364  self%ode => ode ! Store pointer to ODE system
365  call y0%source(self%work, 2) ! Allocate temporary storage
366  sll_assert(t0 >= 0.0)
367 
368  end subroutine init__rk4e_classic
369 
370  !----------------------------------------------------------------------------
371  subroutine step__rk4e_classic(self, t, y, h, ynew)
372  class(sll_t_rk4e_classic), intent(inout) :: self
373  real(wp), intent(in) :: t
374  class(sll_c_vector_space), intent(in) :: y
375  real(wp), intent(in) :: h
376  class(sll_c_vector_space), intent(inout) :: ynew
377 
378  ! Butcher's table
379  real(wp), parameter :: f3 = 1.0_f64/3.0_f64, f6 = 1.0_f64/6.0_f64
380  real(wp), parameter :: &
381  c2 = 0.5_f64, a21 = 0.5_f64, &
382  c3 = 0.5_f64, a32 = 0.5_f64, &
383  c4 = 1.0_f64, a43 = 1.0_f64, &
384  b1 = f6, b2 = f3, b3 = f3, b4 = f6
385 
386  ! Stage 1
387  call self%ode%rhs(t, y, self%work(2))
388  call ynew%mult_add(h*b1, self%work(2), y)
389 
390  ! Stage 2
391  call self%work(1)%mult_add(h*a21, self%work(2), y)
392  call self%ode%rhs(t + h*c2, self%work(1), self%work(2))
393  call ynew%incr_mult(h*b2, self%work(2))
394 
395  ! Stage 3
396  call self%work(1)%mult_add(h*a32, self%work(2), y)
397  call self%ode%rhs(t + h*c3, self%work(1), self%work(2))
398  call ynew%incr_mult(h*b3, self%work(2))
399 
400  ! Stage 4
401  call self%work(1)%mult_add(h*a43, self%work(2), y)
402  call self%ode%rhs(t + h*c4, self%work(1), self%work(2))
403  call ynew%incr_mult(h*b4, self%work(2))
404 
405  end subroutine step__rk4e_classic
406 
407  !----------------------------------------------------------------------------
408  subroutine clean__rk4e_classic(self)
409  class(sll_t_rk4e_classic), intent(inout) :: self
410  deallocate (self%work)
411  end subroutine clean__rk4e_classic
412 
413 end module sll_m_rk_explicit
Abstract types for: 1) generic ODE system, and 2) ODE integrator.
subroutine clean__rk1e_fwd_euler(self)
subroutine step__rk1e_fwd_euler(self, t, y, h, ynew)
subroutine step__rk2e_heun(self, t, y, h, ynew)
subroutine step__rk2e_midpoint(self, t, y, h, ynew)
subroutine step__rk3e_heun3(self, t, y, h, ynew)
subroutine clean__rk3e_heun3(self)
integer, parameter wp
Working precision.
subroutine clean__rk2e_heun(self)
subroutine step__rk4e_classic(self, t, y, h, ynew)
subroutine init__rk2e_heun(self, ode, t0, y0)
subroutine init__rk3e_heun3(self, ode, t0, y0)
subroutine clean__rk2e_midpoint(self)
subroutine init__rk1e_fwd_euler(self, ode, t0, y0)
subroutine step__rk2e_ralston(self, t, y, h, ynew)
subroutine init__rk2e_ralston(self, ode, t0, y0)
subroutine clean__rk4e_classic(self)
subroutine init__rk4e_classic(self, ode, t0, y0)
subroutine clean__rk2e_ralston(self)
subroutine init__rk2e_midpoint(self, ode, t0, y0)
Abstract type implementing a generic vector space.
Module to select the kind parameter.
integer, parameter, public f64
f64 is the kind type for 64-bit reals (double precision)
Abstract base class for all vector spaces.
    Report Typos and Errors