• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gyrokinetics / gs2 / 1821477209

16 May 2025 02:50PM UTC coverage: 8.139% (+0.2%) from 7.92%
1821477209

push

gitlab-ci

David Dickinson
Merged in bugfix/use_uv_in_coverage_test_to_install_packages (pull request #1142)

3704 of 45511 relevant lines covered (8.14%)

122643.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/split_nonlinear_terms.fpp
1
!> A module to deal with advancing the nonlinear term
2
!> separately from the linear terms.
3
module split_nonlinear_terms
4
  use abstract_config, only: abstract_config_type, CONFIG_MAX_NAME_LEN
5
  use rk_schemes, only: rk_scheme_type
6
  implicit none
7
  private
8

9
  public :: init_split_nonlinear_terms, finish_split_nonlinear_terms, set_split_nonlinear_terms_config
10
  public :: advance_nonlinear_term, advance_nonadiabatic_dfn, strang_split
11
  public :: split_nonlinear_terms_config_type, get_split_nonlinear_terms_config
12

13
  logical :: initialized = .false.
14

15
  !> Statistics for the number of steps taken in the integrators
16
  integer :: nfailed_steps = 0, nrhs_calls = 0, nsuccessful_steps = 0
17

18
  !> Interface to describe the call signatures for routines to calculate the
19
  !> field from gnew (invert_field_func) and to calculate the source term used
20
  !> in the advance_nonlinear_term methods. Used to allow us to pass in these
21
  !> methods, giving us a way to test the integrators on different problems.
22
  interface
23
     subroutine invert_field_func (g_in, phi, apar, bpar, gf_lo, local_only)
24
       implicit none
25
       complex, dimension (:,:,:), intent(in) :: g_in
26
       complex, dimension (:,:,:), intent (out) :: phi, apar, bpar
27
       logical, intent(in), optional :: gf_lo, local_only
28
     end subroutine invert_field_func
29

30
     subroutine source_term_func(g_in, g1, phi, apar, bpar, max_vel_local, &
31
          need_to_adjust, calculate_cfl_limit)
32
       implicit none
33
       complex, dimension (:,:,:), intent (in) :: g_in
34
       complex, dimension (:,:,:), intent (out) :: g1
35
       complex, dimension (:,:,:), intent (in) :: phi, apar, bpar
36
       real, intent(out) :: max_vel_local
37
       logical, intent(in) :: need_to_adjust
38
       logical, intent(in), optional :: calculate_cfl_limit
39
     end subroutine source_term_func
40
  end interface
41

42
  ! Related to inputs
43
  integer :: split_method_switch
44
  integer, parameter :: split_method_ab3 = 1, split_method_backwards_euler = 2, &
45
       split_method_rk = 3, split_method_picard = 4
46
  type(rk_scheme_type) :: the_rk_scheme
47
  logical :: show_statistics, advance_nonadiabatic_dfn, strang_split
48
  real :: convergence_tolerance, time_step_safety_factor
49
  real :: relative_tolerance, absolute_tolerance
50

51
  !> Used to represent the input configuration of split_nonlinear_terms
52
  type, extends(abstract_config_type) :: split_nonlinear_terms_config_type
53
     ! namelist : split_nonlinear_terms_knobs
54
     ! indexed : false
55
     !> The absolute tolerance used in error controlled schemes. Attempt to adjust
56
     !> time step to keep error below this tolerance.
57
     real :: absolute_tolerance = 1.0e-3
58
     !> If true then nonlinear integrator advances the nonadiabatic dfn, h,
59
     !> rather than the modified distribution function, g.
60
     logical :: advance_nonadiabatic_dfn = .false.
61
     !> The tolerance used in convergence checks. Currently only used
62
     !> for halting the fixed-point iteration in the beuler method
63
     real :: convergence_tolerance = 1.0e-1
64
     !> The relative tolerance used in error controlled schemes. Attempt to adjust
65
     !> time step to keep error below this tolerance.
66
     real :: relative_tolerance = 1.0e-2
67
     !> Choose which specific RK scheme to use in the RK method.
68
     !> Generally if the tolerances are small a higher order scheme
69
     !> will be more effective than low order. At loose tolerances
70
     !> low order schemes are generally more efficient
71
     !> Valid options include:
72
     !>
73
     !>  - 'cashkarp' -- Cash-Karp scheme of order 4(5)
74
     !>  - 'default' -- Same as heun
75
     !>  - 'heun' -- Heun scheme of order 1(2)
76
     !>
77
     !> See [[rk_schemes]] for other options.
78
     character(len = 20) :: rk_method = 'default'
79
     !> If true then reports the number of right hand side (NL term) calculations,
80
     !> the number of failed internal steps and the number of successful steps at the
81
     !> end of each linear size step.
82
     logical :: show_statistics = .false.
83
     !> What algorithm do we use to advance the nonlinear term if
84
     !> split_nonlinear is true.
85
     !> Valid options include:
86
     !>
87
     !>  - 'AB3' -- Adams-Bashforth (up to) 3rd order. CLF controlled time step.
88
     !>  - 'beuler' -- Backwards Euler. Relative and absolute error controlled time step.
89
     !>            Newton iteration controlled by convergence_tolerance.
90
     !>  - 'default' -- The same as 'RK'.
91
     !>  - 'RK' -- RK scheme with embedded error estimate. Exact scheme can be switched out
92
     !>            using rk_method switch. Relative and absolute error controlled time step.
93
     !>  - 'picard' -- Use simple Picard iteration. Not generally recommended but available
94
     !>            for experimentation.
95
     !>
96
     character(len = 20) :: split_method = 'default'
97
     !> If true then indicates that we want to use a strang split approach where
98
     !> we advance the NL operator by a half step, linear by a full step and then
99
     !> NL by another half step. This _should_ be second order accurate whilst the
100
     !> regular split is only first order accurate.
101
     logical :: strang_split = .false.
102
     !> Multiplies the new time step calculated from either the error or
103
     !> cfl limits. Smaller values are more conservative but may help avoid
104
     !> repeated violation of the error/cfl limits and hence reduce the
105
     !> number of failed steps.
106
     real :: time_step_safety_factor = 0.9
107
   contains
108
     procedure, public :: read => read_split_nonlinear_terms_config
109
     procedure, public :: write => write_split_nonlinear_terms_config
110
     procedure, public :: reset => reset_split_nonlinear_terms_config
111
     procedure, public :: broadcast => broadcast_split_nonlinear_terms_config
112
     procedure, public, nopass :: get_default_name => get_default_name_split_nonlinear_terms_config
113
     procedure, public, nopass :: get_default_requires_index => get_default_requires_index_split_nonlinear_terms_config
114
  end type split_nonlinear_terms_config_type
115

116
  type(split_nonlinear_terms_config_type) :: split_nonlinear_terms_config
117

118
contains
119

120
  !> Initialises the split_nonlinear_terms module. Primarily just reading input
121
  subroutine init_split_nonlinear_terms(split_nonlinear_terms_config_in)
×
122
    implicit none
123
    type(split_nonlinear_terms_config_type), intent(in), optional :: split_nonlinear_terms_config_in
124

125
    if (initialized) return
×
126
    initialized = .true.
×
127

128
    call read_parameters(split_nonlinear_terms_config_in)
×
129

130
  end subroutine init_split_nonlinear_terms
131

132
  !> Read the split_nonlinear_terms namelist
133
  subroutine read_parameters(split_nonlinear_terms_config_in)
×
134
    use file_utils, only: error_unit
135
    use text_options, only: text_option, get_option_value
136
    use rk_schemes, only: get_rk_schemes_as_text_options, get_rk_scheme_by_id
137
    implicit none
138
    type(split_nonlinear_terms_config_type), intent(in), optional :: split_nonlinear_terms_config_in
139
    type(text_option), dimension (*), parameter :: split_method_opts = &
140
         [ &
141
         text_option('default', split_method_rk), &
142
         text_option('AB3', split_method_ab3), &
143
         text_option('RK', split_method_rk), &
144
         text_option('beuler', split_method_backwards_euler),  &
145
         text_option('picard', split_method_picard)  &
146
         ]
147
    character(len = 20) :: rk_method, split_method
148
    integer :: ierr, rk_method_switch
149

150
    if (present(split_nonlinear_terms_config_in)) split_nonlinear_terms_config = split_nonlinear_terms_config_in
×
151

152
    call split_nonlinear_terms_config%init(name = 'split_nonlinear_terms_knobs', requires_index = .false.)
×
153

154
    ! Copy out internal values into module level parameters
155
    associate(self => split_nonlinear_terms_config)
156
#include "split_nonlinear_terms_copy_out_auto_gen.inc"
157
    end associate
158

159
    ierr = error_unit()
×
160

161
    call get_option_value &
162
         (split_method, split_method_opts, split_method_switch, &
163
         ierr, "split_method in split_nonlinear_terms_knobs",.true.)
×
164

165
    call get_option_value &
166
         (rk_method, get_rk_schemes_as_text_options(), rk_method_switch, &
167
         ierr, "rk_method in split_nonlinear_terms_knobs",.true.)
×
168
    the_rk_scheme = get_rk_scheme_by_id(rk_method_switch)
×
169
  end subroutine read_parameters
×
170

171
  !> Reset the module, freeing memory etc.
172
  subroutine finish_split_nonlinear_terms
×
173
    implicit none
174
    initialized = .false.
×
175
    call split_nonlinear_terms_config%reset()
×
176
  end subroutine finish_split_nonlinear_terms
×
177

178
  !> Reset the step statistics
179
  subroutine reset_step_statistics
×
180
    nfailed_steps = 0 ; nrhs_calls = 0 ; nsuccessful_steps = 0
×
181
  end subroutine reset_step_statistics
×
182

183
  subroutine report_step_statistics(istep, unit_in)
×
184
    use iso_fortran_env, only: output_unit
185
    use optionals, only: get_option_with_default
186
    implicit none
187
    integer, intent(in) :: istep
188
    integer, intent(in), optional :: unit_in
189
    integer :: unit
190
    unit = get_option_with_default(unit_in, output_unit)
×
191
    write(unit,'("Iteration : ",I0," Number of internal steps : ",I0," Number of failed steps : ",I0," Number of RHS calls : ",I0)') &
192
         istep, nsuccessful_steps, nfailed_steps, nrhs_calls
×
193
  end subroutine report_step_statistics
×
194

195
  !> Advances dg/dt = NL(g,chi) from g_state, chi from t -> t+dt by calling
196
  !> specific requested method.
197
  !> On output g_state contains new g at next step, chi is left unchanged
198
  subroutine advance_nonlinear_term(g_state, istep, phi, apar, bpar, &
×
199
       dt, fields_func, source_func)
200
    use dist_fn_arrays, only: g_adjust, to_g_gs2, from_g_gs2
201
    use mp, only: mp_abort, get_mp_times, proc0
202
    use job_manage, only: time_message
203
    use nonlinear_terms, only: time_add_explicit_terms, time_add_explicit_terms_mpi
204
    use run_parameters, only: has_phi, has_apar, has_bpar
205
    use array_utils, only: copy
206
    implicit none
207
    integer, intent(in) :: istep
208
    complex, dimension (:,:,:), intent(in out) :: g_state
209
    complex, dimension (:,:,:), intent(in) :: phi, apar, bpar
210
    real, intent(in) :: dt
211
    complex, dimension (:,:,:), allocatable :: phinew, aparnew, bparnew
×
212
    real :: mp_total_after, mp_total
213
    procedure(invert_field_func) :: fields_func
214
    procedure(source_term_func) :: source_func
215

216
    call time_message(.false., time_add_explicit_terms, 'Explicit terms')
×
217
    call get_mp_times(total_time = mp_total)
×
218
    call reset_step_statistics
×
219

220
    ! Copy current fields to local copies
221
    allocate(phinew, mold = phi)
×
222
    allocate(aparnew, mold = apar)
×
223
    allocate(bparnew, mold = bpar)
×
224
    if (has_phi) call copy(phi, phinew)
×
225
    if (has_apar) call copy(apar, aparnew)
×
226
    if (has_bpar) call copy(bpar, bparnew)
×
227

228
    ! If we want to advance h rather than g then adjust
229
    ! here to get h. Note we rely on the call site also
230
    ! providing the correct fields_func to calculate fields
231
    ! from h rather than g. A different option would be
232
    ! to require the call site to always pass a way to calculate
233
    ! from g and a way from h and we then decide here which to use.
234
    if (advance_nonadiabatic_dfn) then
×
235
       call g_adjust(g_state, phinew, bparnew, direction = from_g_gs2)
×
236
    end if
237

238
    ! Call requested method for advancing nonlinear term
239
    select case(split_method_switch)
×
240
    case(split_method_ab3)
241
       call advance_nonlinear_term_ab3(g_state, phinew, aparnew, bparnew, dt, &
×
242
            fields_func, source_func)
×
243
    case(split_method_backwards_euler)
244
       call advance_nonlinear_term_beuler(g_state, phinew, aparnew, bparnew, dt, &
×
245
            fields_func, source_func)
×
246
    case(split_method_rk)
247
       call advance_nonlinear_term_rk(g_state, phinew, aparnew, bparnew, dt, &
×
248
            fields_func, source_func)
×
249
    case(split_method_picard)
250
       call advance_nonlinear_term_picard(g_state, phinew, aparnew, bparnew, dt, &
×
251
            fields_func, source_func)
×
252
    case default
253
       call mp_abort("Invalid split_method_switch", .true.)
×
254
    end select
255

256
    if (proc0 .and. show_statistics) call report_step_statistics(istep)
×
257

258
    ! If we advanced h then we need to reconstruct g from this
259
    if (advance_nonadiabatic_dfn) then
×
260
       call g_adjust(g_state, phi, bpar, direction = to_g_gs2)
×
261
    end if
262

263
    ! Now that we've advanced the explicit terms by dt and set the
264
    ! current g_state to this new result. Note that we do not update the
265
    ! fields, instead resetting them to their original values. The
266
    ! update to the fields is calculated as a part of the implicit
267
    ! field solve and including the NL contribution here leads to
268
    ! "double counting".
269

270
    call time_message(.false., time_add_explicit_terms, 'Explicit terms')
×
271
    call get_mp_times(total_time = mp_total_after)
×
272
    time_add_explicit_terms_mpi = time_add_explicit_terms_mpi + (mp_total_after - mp_total)
×
273
  end subroutine advance_nonlinear_term
×
274

275
  !> Advances dg/dt = NL(g,chi) from current g_state, chi from t -> t+dt by using
276
  !> AB scheme of orders up to 3rd.
277
  subroutine advance_nonlinear_term_ab3(g_state, phinew, aparnew, bparnew, dt, &
×
278
       fields_func, source_func)
279
    use theta_grid, only: ntgrid
280
    use gs2_time, only: get_adams_bashforth_coefficients
281
    use gs2_time, only: code_dt_min, dt_not_set, code_dt_prev1, code_dt_prev2
282
    use mp, only: mp_abort, max_allreduce
283
    use gs2_layouts, only: g_lo
284
    use array_utils, only: copy, zero_array
285
    implicit none
286
    complex, dimension (-ntgrid:, :, g_lo%llim_proc:), intent(in out) :: g_state
287
    complex, dimension (-ntgrid:,:,:), intent (in out) :: phinew, aparnew, bparnew
288
    !> Arrays to hold the nonlinear source history similar to gexp_1/2/3
289
    complex, dimension(:, :, :), allocatable :: gnl_1, gnl_2, gnl_3
×
290
    real, intent(in) :: dt
291
    real :: internal_time, time_step, max_vel, half_time
292
    real, dimension(:), allocatable :: ab_coeffs
×
293
    integer :: counter, iglo
294
    procedure(invert_field_func) :: fields_func
295
    procedure(source_term_func) :: source_func
296

297
    ! Initialise internal variables
298
    internal_time = 0.0
×
299
    counter = 1
×
300

301
    allocate(gnl_1(-ntgrid:ntgrid, 2, g_lo%llim_proc:g_lo%ulim_alloc)) ; call zero_array(gnl_1)
×
302
    allocate(gnl_2(-ntgrid:ntgrid, 2, g_lo%llim_proc:g_lo%ulim_alloc)) ; call zero_array(gnl_2)
×
303
    allocate(gnl_3(-ntgrid:ntgrid, 2, g_lo%llim_proc:g_lo%ulim_alloc)) ; call zero_array(gnl_3)
×
304

305
    ! Reset time step sizes to reflect fact we're restarting the explicit scheme
306
    code_dt_prev1 = dt_not_set
×
307
    code_dt_prev2 = dt_not_set
×
308

309
    ! Keep taking explicit steps until we have advanced by dt
310
    do while (internal_time < dt)
×
311
       ! Get the current nonlinear source term and cfl limit
312
       call source_func(g_state, gnl_1, phinew, aparnew, bparnew, &
×
313
            max_vel, need_to_adjust = .not. advance_nonadiabatic_dfn, &
314
            calculate_cfl_limit = .true.)
×
315
       nrhs_calls = nrhs_calls + 1
×
316

317
       ! Get the maximum velocity across all processors
318
       ! and convert to time_step estimate.
319
       call max_allreduce(max_vel)
×
320
       time_step = 1.0 / max_vel
×
321
       time_step = time_step * time_step_safety_factor
×
322

323
       ! If the cfl time step is too small then abort
324
       if (time_step < code_dt_min) call mp_abort("Time step has become too small in advance_nonlinear_term.", .true.)
×
325

326
       ! Now make sure we're don't overshoot the target time by limiting time step.
327
       time_step = min(time_step, dt - internal_time)
×
328

329
       ! Get the Adams-Bashforth coefficients for the current collection of time step sizes
330
       ab_coeffs = get_adams_bashforth_coefficients(target_dt = time_step)
×
331

332
       half_time = 0.5 * time_step
×
333

334
       ! Advance solution by time_step
335
       select case(counter)
×
336
       case(1)
337
          !$OMP PARALLEL DO DEFAULT(none) &
338
          !$OMP PRIVATE(iglo) &
339
          !$OMP SHARED(g_lo, g_state, half_time, ab_coeffs, gnl_1) &
340
          !$OMP SCHEDULE(static)
341
          do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
342
             g_state(:, :, iglo) = g_state(:, :, iglo) + half_time *( &
×
343
                  ab_coeffs(1) * gnl_1(:, :, iglo)       &
×
344
                  )
×
345
          end do
346
          !$OMP END PARALLEL DO
347
       case(2)
348
          !$OMP PARALLEL DO DEFAULT(none) &
349
          !$OMP PRIVATE(iglo) &
350
          !$OMP SHARED(g_lo, g_state, half_time, ab_coeffs, gnl_1, gnl_2) &
351
          !$OMP SCHEDULE(static)
352
          do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
353
             g_state(:, :, iglo) = g_state(:, :, iglo) + half_time *( &
×
354
                  ab_coeffs(1) * gnl_1(:, :, iglo) +     &
×
355
                  ab_coeffs(2) * gnl_2(:, :, iglo)       &
×
356
                  )
×
357
          end do
358
          !$OMP END PARALLEL DO
359
       case default
360
          !$OMP PARALLEL DO DEFAULT(none) &
361
          !$OMP PRIVATE(iglo) &
362
          !$OMP SHARED(g_lo, g_state, half_time, ab_coeffs, gnl_1, gnl_2, gnl_3) &
363
          !$OMP SCHEDULE(static)
364
          do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
365
             g_state(:, :, iglo) = g_state(:, :, iglo) + half_time *( &
×
366
                  ab_coeffs(1) * gnl_1(:, :, iglo) +     &
×
367
                  ab_coeffs(2) * gnl_2(:, :, iglo) +     &
×
368
                  ab_coeffs(3) * gnl_3(:, :, iglo)       &
×
369
                  )
×
370
          end do
371
          !$OMP END PARALLEL DO
372
       end select
373

374
       ! Calculate the fields consistent with the new g
375
       call calculate_fields(fields_func, g_state, phinew, aparnew, bparnew)
×
376

377
       ! Increment internal state
378
       internal_time = internal_time + time_step
×
379
       counter = counter + 1
×
380
       nsuccessful_steps = nsuccessful_steps + 1
×
381

382
       ! Shift time history along one point
383
       code_dt_prev2 = code_dt_prev1
×
384
       code_dt_prev1 = time_step
×
385
       call copy(gnl_2, gnl_3)
×
386
       call copy(gnl_1, gnl_2)
×
387
    end do
388

389
  end subroutine advance_nonlinear_term_ab3
×
390

391
  !> Advances dg/dt = NL(g,chi) from current g_state, chi from t -> t+dt by using
392
  !> backwards Euler with fixed-point iteration.
393
  subroutine advance_nonlinear_term_beuler(g_state, phinew, aparnew, bparnew, dt, &
×
394
       fields_func, source_func)
395
    use theta_grid, only: ntgrid
396
    use gs2_time, only: code_dt_min
397
    use mp, only: mp_abort, max_allreduce, proc0
398
    use run_parameters, only: immediate_reset
399
    use gs2_layouts, only: g_lo
400
    use array_utils, only: copy, gs2_max_abs, zero_array
401
    use warning_helpers, only: exactly_equal
402
    implicit none
403
    complex, dimension (-ntgrid:, :, g_lo%llim_proc:), intent(in out) :: g_state
404
    complex, dimension (-ntgrid:,:,:), intent (in out) :: phinew, aparnew, bparnew
405
    complex, dimension(:, :, :), allocatable :: g_cur, g_local, gnl_1, gnl_2
×
406
    real, intent(in) :: dt
407
    real :: internal_time, time_step, max_vel
408
    integer :: counter, iglo
409
    integer, parameter :: iteration_limit = 20
410
    real, parameter :: time_adjust_factor = 2
411
    logical, parameter :: debug = .false.
412
    real :: inverse_error, error, actual_dt, half_time, cfl_time_step
413
    real, dimension(3) :: errors
414
    real, save :: time_step_last = -1
415
    procedure(invert_field_func) :: fields_func
416
    procedure(source_term_func) :: source_func
417
    logical :: reject_step, cfl_limited
418
    complex, dimension(:, :, :), allocatable :: error_array
×
419

420
    ! Initialise internal variables
421
    if (time_step_last < 0) time_step_last = dt
×
422
    internal_time = 0.0
×
423
    time_step = time_step_last
×
424

425
    allocate(error_array(-ntgrid:ntgrid, 2, g_lo%llim_proc:g_lo%ulim_alloc))
×
426
    allocate(g_cur(-ntgrid:ntgrid, 2, g_lo%llim_proc:g_lo%ulim_alloc))
×
427
    allocate(g_local(-ntgrid:ntgrid, 2, g_lo%llim_proc:g_lo%ulim_alloc))
×
428
    allocate(gnl_1(-ntgrid:ntgrid, 2, g_lo%llim_proc:g_lo%ulim_alloc))
×
429
    allocate(gnl_2(-ntgrid:ntgrid, 2, g_lo%llim_proc:g_lo%ulim_alloc))
×
430
    call copy(g_state, g_cur)
×
431
    call zero_array(error_array) ; call zero_array(g_local)
×
432
    call zero_array(gnl_1); call zero_array(gnl_2)
×
433

434
    ! Keep on advancing until we have advanced by dt
435
    do while (internal_time < dt)
×
436
       ! Reset counter and error
437
       counter = 0
×
438
       error = 1.0
×
439
       errors = 0.0
×
440

441
       ! Limit the actual time step that we take to avoid overshooting (i.e.
442
       ! to keep internal_time <= dt).
443
       actual_dt = min(time_step, dt - internal_time)
×
444
       half_time = 0.5 * actual_dt
×
445

446
       ! Calculate the current NL source term, for use in error
447
       ! estimates later.
448
       call source_func(g_state, gnl_2, phinew, aparnew, bparnew, &
×
449
            max_vel, need_to_adjust = .not. advance_nonadiabatic_dfn, &
450
            calculate_cfl_limit = .false.)
×
451
       nrhs_calls = nrhs_calls + 1
×
452

453
       ! Try to take a step of size actual_dt using fixed point iteration with
454
       ! backwards Euler.
455
       do while (error > convergence_tolerance .and. counter < iteration_limit)
×
456

457
          ! Get the current nonlinear source term and cfl limit -- note we don't use the
458
          ! cfl limit here
459
          call source_func(g_state, gnl_1, phinew, aparnew, bparnew, &
×
460
               max_vel, need_to_adjust = .not. advance_nonadiabatic_dfn, &
461
               calculate_cfl_limit = .false.)
×
462
          nrhs_calls = nrhs_calls + 1
×
463

464
          ! Calculate the estimate of the new g
465
          !$OMP PARALLEL DO DEFAULT(none) &
466
          !$OMP PRIVATE(iglo) &
467
          !$OMP SHARED(g_lo, g_local, g_cur, half_time, gnl_1) &
468
          !$OMP SCHEDULE(static)
469
          do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
470
             g_local(:, :, iglo) = g_cur(:, :, iglo) + half_time * gnl_1(:, :, iglo)
×
471
          end do
472
          !$OMP END PARALLEL DO
473

474
          ! Calculate the maximum absolute error and the maximum value of the previous solution
475

476
          ! Note we _could_ merge the following loop witth the gs2_max_abs kernel
477
          ! so that we only loop over the error_array once (and actually don't
478
          ! need to explicitly store the full array)
479
          !$OMP PARALLEL DO DEFAULT(none) &
480
          !$OMP PRIVATE(iglo) &
481
          !$OMP SHARED(g_lo, error_array, g_state, g_local) &
482
          !$OMP SCHEDULE(static)
483
          do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
484
             error_array(:, :, iglo) = g_state(:, :, iglo) - g_local(:, :, iglo)
×
485
          end do
486
          !$OMP END PARALLEL DO
487
          errors(1) = gs2_max_abs(error_array)
×
488
          errors(2) = gs2_max_abs(g_local)
×
489
          call max_allreduce(errors)
×
490

491
          ! Calculate an approximate relative error
492
          error = errors(1) / errors(2)
×
493

494
          ! Update internals -- increment counter and update the 'previous' solution with the new one.
495
          counter = counter + 1
×
496
          call copy(g_local, g_state)
×
497

498
          ! Update the fields consistently with the current solution
499
          call fields_func(g_state, phinew, aparnew, bparnew, local_only = .true.)
×
500
       end do
501

502
       ! Now we need to decide if the last step was successful or not. There are two exit conditions, either the
503
       ! error was small enough (good step) or the iteration limit was exceeded (bad step).
504
       if (counter >= iteration_limit) then
×
505
          ! If it was a bad step then throw away these changes and reduce the time step
506
          if (proc0 .and. debug) print*,'Too many iterations, reducing step from',time_step,'to',time_step/time_adjust_factor,'with internal_time = ',internal_time
507

508
          ! Reduce the time step
509
          time_step = time_step / time_adjust_factor
×
510
          ! If the time step is now too small then abort
511
          if (time_step < code_dt_min) call mp_abort("Time step has become too small in advance_nonlinear_term.", .true.)
×
512

513
          ! If we've taken too many iterations then we want to drop the time step and try
514
          ! again. To try again we must ensure that gnew has been reset to g, and the fields
515
          ! are consistent with this.
516
          reject_step = .true.
×
517
       else
518
          ! If we have met the convergence condition then we should estimate the error
519
          ! on the solution and see if we need to reject the step or how we should adjust
520
          ! the step size.
521
          ! To estimate the error we compare the backward Euler update with an approximate
522
          ! Crank-Nicolson update.
523
          ! g_new_beuler = g + 0.5 * actual_dt * gnl_1(g_new_beuler)
524
          ! g_new_cn = g + 0.5 * actual_dt * (gnl_1(g_new_beuler) + gnl_1(g))/2
525
          ! abs_error  = |g_new_beuler - g_new_cn|
526
          !      = 0.5 * actual_dt * 0.5 * (gnl_1(g_new_beuler) - gnl_1(g)
527
          ! Where gnl_1(h) is the source func evaluated with gnew=h.
528

529
          ! Here we calculate gnl_1(g_new_beuler) exactly. We could probably
530
          ! rely on using the existing value of gnl_1(g_local) as, whilst this is
531
          ! strictly from the previous iteration of the fixed-point scheme, we know
532
          ! it is within the convergence tolerance of the true value.
533
          call source_func(g_state, gnl_1, phinew, aparnew, bparnew, &
×
534
               max_vel, need_to_adjust = .not. advance_nonadiabatic_dfn, &
535
               calculate_cfl_limit = .true.)
×
536
          nrhs_calls = nrhs_calls + 1
×
537

538
          ! Note we _could_ merge the following loop witth the gs2_max_abs kernel
539
          ! so that we only loop over the error_array once (and actually don't
540
          ! need to explicitly store the full array)
541
          !$OMP PARALLEL DO DEFAULT(none) &
542
          !$OMP PRIVATE(iglo) &
543
          !$OMP SHARED(g_lo, error_array, gnl_1, gnl_2) &
544
          !$OMP SCHEDULE(static)
545
          do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
546
             error_array(:, :, iglo) = gnl_1(:, :, iglo) - gnl_2(:, :, iglo)
×
547
          end do
548
          !$OMP END PARALLEL DO
549
          errors(1) = gs2_max_abs(error_array) * (0.5 * half_time)
×
550
          errors(3) = max_vel
×
551
          call max_allreduce(errors)
×
552
          inverse_error = get_inverse_error(errors)
×
553
          cfl_time_step = 1.0 / errors(3)
×
554

555
          !Calculate new time step based on error
556
          time_step = min(actual_dt * sqrt(inverse_error), cfl_time_step)
×
557
          cfl_limited = time_step < actual_dt .and. exactly_equal(time_step, cfl_time_step)
×
558
          time_step = time_step * time_step_safety_factor
×
559

560
          ! If error is too large reject the step
561
          if ( inverse_error < 1.0 .or. (cfl_limited .and. immediate_reset)) then
×
562
             if (proc0 .and. debug) print*,'Attempted beuler step failed due to error or cfl, adapting time step'
563
             ! Reset as we're retrying the step.
564
             reject_step = .true.
×
565
          else
566
             reject_step = .false.
×
567
          end if
568
       end if
569

570
       if (reject_step) then
×
571
          ! If the time step is now too small then abort
572
          if (time_step < code_dt_min) call mp_abort("Time step has become too small in advance_nonlinear_term.", .true.)
×
573
          call copy(g_cur, g_state)
×
574
          ! If we store the initial field then we could perhaps just copy here instead.
575
          ! This could be faster as it avoids communications involved in the velocity integration.
576
          call calculate_fields(fields_func, g_state, phinew, aparnew, bparnew)
×
577
          nfailed_steps = nfailed_steps + 1
×
578
       else
579
          ! If we have met the error condition and not taken too many steps
580
          ! then g_state represents our new solution so make sure we update g to reflect this
581
          internal_time = internal_time + actual_dt
×
582
          call copy(g_state, g_cur)
×
583
          nsuccessful_steps = nsuccessful_steps + 1
×
584
       end if
585

586
    end do
587

588
    ! Store the final time step size so we can start from this in the next callg
589
    time_step_last = time_step
×
590

591
    if(proc0.and.debug) print*,'Explicit done in ',counter,'steps with final error',inverse_error,'and step',time_step,'final time',internal_time
592

593
  end subroutine advance_nonlinear_term_beuler
×
594

595
  !> Advances dg/dt = NL(g,chi) from current g_state, chi from t -> t+dt by using
596
  !> an Runge-Kutta scheme with embedded error estimate for error control. This method
597
  !> is a wrapper to the true generic RK implementation.
598
  subroutine advance_nonlinear_term_rk(g_state, phinew, aparnew, bparnew, dt, &
×
599
       fields_func, source_func)
600
    implicit none
601
    complex, dimension (:,:,:), intent(in out) :: g_state
602
    complex, dimension (:,:,:), intent (in out) :: phinew, aparnew, bparnew
603
    real, intent(in) :: dt
604
    procedure(invert_field_func) :: fields_func
605
    procedure(source_term_func) :: source_func
606

607
    call advance_nonlinear_term_rk_implementation( &
608
         g_state, phinew, aparnew, bparnew, dt, &
×
609
         fields_func, source_func, the_rk_scheme)
×
610
  end subroutine advance_nonlinear_term_rk
×
611

612
  !> Advances dg/dt = NL(g,chi) from current g_state, chi from t -> t+dt by using
613
  !> an Runge-Kutta scheme with embedded error estimate for error control
614
  subroutine advance_nonlinear_term_rk_implementation(g_state, phinew, &
×
615
       aparnew, bparnew, dt, fields_func, source_func, scheme)
×
616
    use theta_grid, only: ntgrid
617
    use gs2_time, only: code_dt_min
618
    use mp, only: mp_abort, proc0, max_allreduce
619
    use gs2_layouts, only: g_lo
620
    use rk_schemes, only: rk_scheme_type
621
    use run_parameters, only: immediate_reset
622
    use array_utils, only: copy, gs2_max_abs, zero_array
623
    use warning_helpers, only: exactly_equal
624
    implicit none
625
    complex, dimension (-ntgrid:, :, g_lo%llim_proc:), intent(in out) :: g_state
626
    complex, dimension (-ntgrid:,:,:), intent (in out) :: phinew, aparnew, bparnew
627
    complex, dimension(:, :, :), allocatable :: g_cur, gnl_1
×
628
    real, intent(in) :: dt
629
    type(rk_scheme_type), intent(in) :: scheme
630
    complex, dimension (:, :, :, :), allocatable :: stages
×
631
    real :: internal_time, time_step, max_vel, inverse_error
632
    real :: new_time_step, cfl_time_step
633
    integer :: counter, nstages
634
    logical, parameter :: debug = .false.
635
    integer :: istage, istage_sub, iglo
636
    real, dimension(:), allocatable :: solution_coeffs, error_coeffs
×
637
    real, dimension(3) :: errors
638
    real, save :: time_step_last = -1
639
    procedure(invert_field_func) :: fields_func
640
    procedure(source_term_func) :: source_func
641
    logical :: cfl_limited
642
    real :: half_time
643
    real, dimension(:), allocatable :: stage_coeffs
×
644

645
    ! Initialise internal variables
646
    if (time_step_last < 0) time_step_last = dt
×
647
    internal_time = 0.0
×
648
    counter = 1
×
649
    time_step = time_step_last
×
650

651
    ! Copy the relevant coefficients for forming the solution to reduce
652
    ! later duplication.
653
    if (scheme%follow_high_order) then
×
654
       allocate(solution_coeffs, source = scheme%high_order_coeffs)
×
655
    else
656
       allocate(solution_coeffs, source = scheme%lower_order_coeffs)
×
657
    end if
658

659
    ! Get the error coefficients
660
    allocate(error_coeffs, source = scheme%high_order_coeffs - scheme%lower_order_coeffs)
×
661

662
    nstages = scheme%number_of_stages
×
663
    allocate(stages(-ntgrid:ntgrid, 2, g_lo%llim_proc:g_lo%ulim_alloc, nstages))
×
664
    allocate(gnl_1(-ntgrid:ntgrid, 2, g_lo%llim_proc:g_lo%ulim_alloc))
×
665
    allocate(g_cur(-ntgrid:ntgrid, 2, g_lo%llim_proc:g_lo%ulim_alloc))
×
666
    call copy(g_state, g_cur) ; call zero_array(stages) ; call zero_array(gnl_1)
×
667

668
    ! Keep taking explicit steps until we have advanced by dt
669
    do while (internal_time < dt)
×
670
       if(proc0.and.debug) print*,'Taking step with size',time_step,'at time',internal_time
671
       half_time = 0.5 * time_step
×
672

673
       do istage = 1, nstages
×
674
          call copy(g_cur, g_state)
×
675
          stage_coeffs = half_time * scheme%coeffs(:, istage)
×
676

677
          !$OMP PARALLEL DO DEFAULT(none) &
678
          !$OMP PRIVATE(istage_sub, iglo) &
679
          !$OMP SHARED(g_lo, g_state, half_time, scheme, stages, istage, stage_coeffs) &
680
          !$OMP COLLAPSE(2) &
681
          !$OMP SCHEDULE(static)
682
          do istage_sub = 1, istage-1
×
683
             do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
684
                g_state(:, :, iglo) = g_state(:, :, iglo) + stage_coeffs(istage_sub) * stages(:, :, iglo, istage_sub)
×
685
             end do
686
          end do
687
          !$OMP END PARALLEL DO
688
          call calculate_fields(fields_func, g_state, phinew, aparnew, bparnew)
×
689

690
          ! Get the current nonlinear source term
691
          call source_func(g_state,  stages(:, :, :, istage), &
×
692
               phinew, aparnew, bparnew, &
×
693
               max_vel, need_to_adjust = .not. advance_nonadiabatic_dfn, &
694
               calculate_cfl_limit = (istage == 1))
×
695

696
          nrhs_calls = nrhs_calls + 1
×
697
          if (istage == 1) errors(3) = max_vel
×
698

699
       end do
700

701
       ! One can directly form the error by simply forming error =
702
       ! time_step * 0.5 * sum(stages_i * (high_order_coeffs_i -
703
       ! low_order_coeffs_i)). Here we store this in gnl_1
704
       ! without the time_step * 0.5 factor (to avoid extra
705
       ! array operations). We multiply the final error by
706
       ! time_step/2 to ensure full consistency.
707
       ! Could we merge this OpenMP parallel region with the
708
       ! other loop directly below to avoid some overhead?
709
       !$OMP PARALLEL DO DEFAULT(none) &
710
       !$OMP PRIVATE(iglo) &
711
       !$OMP SHARED(g_lo, gnl_1, error_coeffs, stages) &
712
       !$OMP SCHEDULE(static)
713
       do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
714
          gnl_1(:, :, iglo) = error_coeffs(1) * stages(:, :, iglo, 1)
×
715
       end do
716
       !$OMP END PARALLEL DO
717

718
       !$OMP PARALLEL DO DEFAULT(none) &
719
       !$OMP PRIVATE(iglo, istage) &
720
       !$OMP SHARED(g_lo, gnl_1, error_coeffs, nstages, stages) &
721
       !$OMP COLLAPSE(2) &
722
       !$OMP SCHEDULE(static)
723
       do istage = 2, nstages
×
724
          do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
725
             gnl_1(:, :, iglo) = gnl_1(:, :, iglo) + error_coeffs(istage) * stages(:, :, iglo, istage)
×
726
          end do
727
       end do
728
       !$OMP END PARALLEL DO
729

730
       ! Store the absolute error
731
       errors(1) = gs2_max_abs(gnl_1) * half_time
×
732
       ! Estimate the absolution size of the solution using
733
       ! the old solution
734
       errors(2) = gs2_max_abs(g_cur)
×
735
       call max_allreduce(errors)
×
736
       inverse_error = get_inverse_error(errors)
×
737
       cfl_time_step = 1.0 / errors(3)
×
738

739
       !Calculate the new_time_step
740
       new_time_step = min(time_step * (inverse_error)**(1.0/scheme%order), cfl_time_step)
×
741
       cfl_limited = new_time_step < time_step .and. exactly_equal(new_time_step, cfl_time_step)
×
742
       new_time_step = new_time_step * time_step_safety_factor
×
743

744
       if(proc0 .and. debug) print*,'Inverse error is ',inverse_error,'at internal_time', &
745
            internal_time,'with step',time_step,'new time step is',new_time_step, &
746
            'counter=',counter
747

748
       ! If the cfl time step is too small then abort
749
       if (new_time_step < code_dt_min) &
×
750
            call mp_abort("Time step has become too small in advance_nonlinear_term.", .true.)
×
751

752
       ! If the error is too large we need to retry with a smaller step, so don't
753
       ! update anything
754
       if (inverse_error < 1.0 .or. (cfl_limited .and. immediate_reset)) then
×
755
          if(proc0.and.debug) print*,'Time step with size',time_step, &
756
               'failed at internal_time',internal_time,'and counter',counter, &
757
               'retrying with step',new_time_step
758
          nfailed_steps = nfailed_steps + 1
×
759
       else
760
          stage_coeffs = solution_coeffs * half_time
×
761
          !Update the solution. Note we could probably merge the two
762
          !OpenMP loops into a single OpenMP parallel region and then
763
          !just add OMP DO to each loop. This would save a bit of overhead.
764
          !$OMP PARALLEL DO DEFAULT(none) &
765
          !$OMP PRIVATE(iglo) &
766
          !$OMP SHARED(g_lo, g_cur, solution_coeffs, stage_coeffs, stages) &
767
          !$OMP SCHEDULE(static)
768
          do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
769
             g_cur(:, :, iglo) = g_cur(:, :, iglo) + stage_coeffs(1) * stages(:, :, iglo, 1)
×
770
          end do
771
          !$OMP END PARALLEL DO
772

773
          !$OMP PARALLEL DO DEFAULT(none) &
774
          !$OMP PRIVATE(istage, iglo) &
775
          !$OMP SHARED(g_lo, g_cur, solution_coeffs, nstages, half_time, &
776
          !$OMP stages, stage_coeffs) &
777
          !$OMP COLLAPSE(2) &
778
          !$OMP SCHEDULE(static)
779
          do istage = 2, nstages
×
780
             do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
781
                g_cur(:, :, iglo) = g_cur(:, :, iglo) + stage_coeffs(istage) * stages(:, :, iglo, istage)
×
782
             end do
783
          end do
784
          !$OMP END PARALLEL DO
785

786
          internal_time = internal_time + time_step
×
787
          nsuccessful_steps = nsuccessful_steps + 1
×
788
       end if
789

790
       !Now update the time step, capped to dt
791
       time_step = min(new_time_step, dt)
×
792

793
       ! Make sure we don't overshoot the target time by limiting time step.
794
       if(internal_time < dt) then
×
795
          time_step = min(time_step, dt - internal_time)
×
796
          counter = counter + 1
×
797
       end if
798

799
    end do
800

801
    if(proc0.and.debug) print*,'Completed rk step in',counter,'steps to',internal_time
802

803
    ! We may be able to avoid this copy and field solve on exit if we restructure the main
804
    ! loop slightly so g_state represents the new solution. This may require use of g_work
805
    ! or similar to hold intermediate values.
806
    call copy(g_cur, g_state)
×
807

808
    time_step_last = time_step
×
809
  end subroutine advance_nonlinear_term_rk_implementation
×
810

811
  !> Advances dg/dt = NL(g,chi) from current g_state, chi from t -> t+dt by using
812
  !> a simple Picard iteration scheme
813
  subroutine advance_nonlinear_term_picard(g_state, phinew, &
×
814
       aparnew, bparnew, dt, fields_func, source_func)
×
815
    use theta_grid, only: ntgrid
816
    use gs2_time, only: code_dt_min
817
    use mp, only: mp_abort, proc0, max_allreduce
818
    use gs2_layouts, only: g_lo
819
    use array_utils, only: copy, gs2_max_abs, zero_array
820
    implicit none
821
    complex, dimension (-ntgrid:, :, g_lo%llim_proc:), intent(in out) :: g_state
822
    complex, dimension (-ntgrid:,:,:), intent (in out) :: phinew, aparnew, bparnew
823
    complex, dimension(:, :, :), allocatable :: gnl_1
×
824
    real, intent(in) :: dt
825
    real :: internal_time, time_step, max_vel, cfl_limit
826
    integer :: counter, ipic, iglo
827
    logical, parameter :: debug = .false.
828
    real, dimension(4) :: errors
829
    real, save :: time_step_last = -1
830
    procedure(invert_field_func) :: fields_func
831
    procedure(source_term_func) :: source_func
832
    complex, dimension(:, :, :), allocatable :: g_start
×
833
    integer, parameter :: npic_limit = 15, npic_low_limit = 3
834
    real, parameter :: time_step_factor = 2.0
835
    logical, parameter :: check_cfl = .true.
836
    logical :: converged
837
    real :: half_time
838

839
    ! Initialise internal variables
840
    if (time_step_last < 0) time_step_last = dt
×
841
    internal_time = 0.0
×
842
    counter = 1
×
843
    time_step = time_step_last
×
844

845
    allocate(g_start(-ntgrid:ntgrid, 2, g_lo%llim_proc:g_lo%ulim_alloc))
×
846
    allocate(gnl_1(-ntgrid:ntgrid, 2, g_lo%llim_proc:g_lo%ulim_alloc))
×
847
    call zero_array(g_start) ; call zero_array(gnl_1)
×
848
    ! Keep taking explicit steps until we have advanced by dt
849
    do while (internal_time < dt)
×
850
       call copy(g_state, g_start)
×
851
       half_time = 0.5 * time_step
×
852

853
       if(proc0.and.debug) print*,'Taking step with size',time_step,'at time',internal_time
854
       ipic = 1
×
855

856
       call calculate_fields(fields_func, g_state, phinew, aparnew, bparnew)
×
857

858
       ! Get the current nonlinear source term
859
       call source_func(g_state, gnl_1, &
×
860
            phinew, aparnew, bparnew, &
×
861
            max_vel, need_to_adjust = .not. advance_nonadiabatic_dfn, &
862
            calculate_cfl_limit = check_cfl)
×
863
       nrhs_calls = nrhs_calls + 1
×
864

865
       errors(1) = 0.0
×
866
       errors(2) = gs2_max_abs(gnl_1) * half_time
×
867
       errors(3) = gs2_max_abs(g_start)
×
868
       errors(4) = max_vel
×
869
       call max_allreduce(errors)
×
870

871
       ! Check for CFL limit - we can simply rescale the change in g and carry on
872
       ! so here we simply rescale the effective time step,
873
       cfl_limit = 1.0 / errors(4)
×
874
       if (time_step > cfl_limit .and. check_cfl) then
×
875
          ! Drop the effective time step a little from the limit
876
          cfl_limit = 0.9 * cfl_limit
×
877
          if(proc0.and.debug) print*,'Dropping time step due to cfl from',time_step,'to',cfl_limit
878
          errors(2) = errors(2) * cfl_limit / time_step
×
879
          time_step = cfl_limit
×
880
          half_time = time_step * 0.5
×
881
       end if
882

883
       converged = picard_converged(errors)
×
884

885
       !$OMP PARALLEL DO DEFAULT(none) &
886
       !$OMP PRIVATE(iglo) &
887
       !$OMP SHARED(g_state, g_start, gnl_1, half_time, g_lo)&
888
       !$OMP SCHEDULE(static)
889
       do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
890
          g_state(:, :, iglo) = g_start(:, :, iglo) + gnl_1(:, :, iglo) * half_time
×
891
       end do
892
       !$OMP END PARALLEL DO
893

894
       do while(.not. converged)
×
895
          ! Note the first step (i.e. call before this loop) is included in
896
          ! ipic here (i.e. it counts towards our approach towards npic_limit).
897
          ipic = ipic + 1
×
898
          if (ipic > npic_limit) exit
×
899

900
          call calculate_fields(fields_func, g_state, phinew, aparnew, bparnew)
×
901

902
          ! Get the current nonlinear source term
903
          call source_func(g_state, gnl_1, &
×
904
               phinew, aparnew, bparnew, &
×
905
               max_vel, need_to_adjust = .not. advance_nonadiabatic_dfn, &
906
               calculate_cfl_limit = check_cfl)
×
907
          nrhs_calls = nrhs_calls + 1
×
908

909
          errors(1) = errors(2)
×
910
          errors(2) = gs2_max_abs(gnl_1) * half_time
×
911
          errors(4) = max_vel
×
912
          call max_allreduce(errors)
×
913
          cfl_limit = 1.0 / errors(4)
×
914

915
          converged = picard_converged(errors)
×
916

917
          if(proc0.and.debug) print*,'ipic',ipic,converged,'|',errors
918

919
          if (time_step > cfl_limit .and. .not. converged) exit
×
920

921
          !$OMP PARALLEL DO DEFAULT(none) &
922
          !$OMP PRIVATE(iglo) &
923
          !$OMP SHARED(g_state, g_start, gnl_1, half_time, g_lo)&
924
          !$OMP SCHEDULE(static)
925
          do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
926
             g_state(:, :, iglo) = g_start(:, :, iglo) + gnl_1(:, :, iglo) * half_time
×
927
          end do
928
          !$OMP END PARALLEL DO
929
       end do
930

931
       if (.not. converged) then
×
932
          ! If we didn't converge in the limit then reset state
933
          ! to the start of the step and reduce the time step.
934
          call copy(g_start, g_state)
×
935
          time_step = time_step / time_step_factor
×
936
          nfailed_steps = nfailed_steps + 1
×
937

938
          ! If the time step is too small then abort
939
          if (time_step < code_dt_min) &
×
940
               call mp_abort("Time step has become too small in advance_nonlinear_term.", .true.)
×
941
       else
942
          internal_time = internal_time + time_step
×
943
          nsuccessful_steps = nsuccessful_steps + 1
×
944
          ! If we converged quickly then increase the time step
945
          if (ipic <= npic_low_limit) time_step = time_step * time_step_factor
×
946
       end if
947

948
       !Now update the time step, capped to dt
949
       time_step = min(time_step, dt)
×
950

951
       ! Make sure we don't overshoot the target time by limiting time step.
952
       if(internal_time < dt) then
×
953
          time_step = min(time_step, dt - internal_time)
×
954
          counter = counter + 1
×
955
       end if
956

957
    end do
958

959
    if(proc0.and.debug) print*,'Completed Picard step in',counter,'steps to',internal_time,'last time step',time_step
960

961
    time_step_last = time_step
×
962
  contains
963
    pure logical function picard_converged(errors) result(converged)
×
964
      implicit none
965
      real, dimension(4), intent(in) :: errors
966
      converged = &
967
           !Change in solution less than abs tol
968
           (errors(2) < absolute_tolerance) .or.  &
969
           ! Change in the change, less than abs tol
970
           (abs(errors(2) - errors(1)) < absolute_tolerance) .or. &
971
           ! Change is less than the relative tolerance
972
           (errors(2) < relative_tolerance * errors(3)) .or. &
973
           ! Change in the change less than the relative tolerance
974
           (abs(errors(2) - errors(1)) < relative_tolerance * errors(3))
×
975
    end function picard_converged
×
976

977
  end subroutine advance_nonlinear_term_picard
978

979
  !> Returns the normalised inverse error estimate. In other words
980
  !> the error tolerance, rtol*errors(2) + atol, divided by the error
981
  !> estimate (plus a small number to avoid divide by zero).
982
  !>
983
  !> Assumes that errors contains the global magnitude of the error
984
  !> estimate in the first element and the global maximum of the solution
985
  !> in the second element.
986
  real pure function get_inverse_error(errors) result(inverse_error)
×
987
    implicit none
988
    real, dimension(:), intent(in) :: errors
989
    inverse_error = (relative_tolerance * errors(2) + absolute_tolerance) / &
×
990
         (errors(1) + epsilon(errors(1)))
×
991
  end function get_inverse_error
×
992

993
  !> Calculates the fields consistent with the passed distribution function
994
  subroutine calculate_fields(fields_func, g_in, phi_out, apar_out, bpar_out)
×
995
    use nonlinear_terms, only: time_add_explicit_terms_field
996
    use job_manage, only: time_message
997
    procedure(invert_field_func) :: fields_func
998
    complex, dimension(:, :, :), intent(in) :: g_in
999
    complex, dimension (:,:,:), intent (out) :: phi_out, apar_out, bpar_out
1000
    call time_message(.false., time_add_explicit_terms_field, 'Explicit terms - field')
×
1001
    call fields_func(g_in, phi_out, apar_out, bpar_out, local_only = .true.)
×
1002
    call time_message(.false., time_add_explicit_terms_field, 'Explicit terms - field')
×
1003
  end subroutine calculate_fields
×
1004

1005
#include "split_nonlinear_terms_auto_gen.inc"
1006
end module split_nonlinear_terms
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc