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

gyrokinetics / gs2 / 2021218321

04 Sep 2025 07:44AM UTC coverage: 10.606% (+0.03%) from 10.577%
2021218321

push

gitlab-ci

David Dickinson
Merged in feature/move_more_initialisation_to_init_levels (pull request #1161)

4710 of 44407 relevant lines covered (10.61%)

125698.1 hits per line

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

0.0
/src/collisions.fpp
1
!> Routines for implementing the model collision operator defined by
2
!> [Barnes, Abel et
3
!> al. 2009](https://pubs.aip.org/aip/pop/article/16/7/072107/263154/Linearized-model-Fokker-Planck-collision-operators)
4
!> or on [arxiv](https://arxiv.org/abs/0809.3945v2).
5
!> The collision operator causes physically motivated smoothing of
6
!> structure in velocity space which is necessary to prevent buildup
7
!> of structure at fine scales in velocity space, while conserving
8
!> energy and momentum.
9
module collisions
10
  use abstract_config, only: abstract_config_type, CONFIG_MAX_NAME_LEN
11
  use redistribute, only: redist_type
12

13
  implicit none
14

15
  private 
16

17
  public :: init_collisions, finish_collisions, adjust_vnmult
18
  public :: read_parameters, wnml_collisions, check_collisions, set_heating, nxi_lim, c_rate
19
  public :: dtot, fdf, fdb, vnmult, ncheck, vary_vnew, init_lorentz_error, set_vnmult
20
  public :: colls, hyper_colls, heating, adjust, split_collisions,use_le_layout, solfp1
21
  public :: collision_model_switch, collision_model_lorentz, collision_model_none
22
  public :: collision_model_lorentz_test, collision_model_full, collision_model_ediffuse
23
  public :: collisions_config_type, set_collisions_config, get_collisions_config
24
  public :: skip_parallel_boundary
25
  
26
  interface solfp1
27
     module procedure solfp1_le_layout
28
     module procedure solfp1_standard_layout
29
  end interface
30

31
  interface conserve_lorentz
32
     module procedure conserve_lorentz_le_layout
33
     module procedure conserve_lorentz_standard_layout
34
  end interface
35

36
  interface conserve_diffuse
37
     module procedure conserve_diffuse_le_layout
38
     module procedure conserve_diffuse_standard_layout
39
  end interface 
40

41
  ! knobs
42
  logical :: use_le_layout
43
  logical :: const_v, conserve_moments
44
  logical :: conservative, resistivity
45
  integer :: collision_model_switch
46
  integer :: lorentz_switch, ediff_switch
47
  logical :: adjust
48
  logical :: heating
49
  logical :: hyper_colls
50
  logical :: ei_coll_only
51
  logical :: test
52
  logical :: special_wfb_lorentz
53
  logical :: vpar_zero_mean
54
  logical :: conserve_forbid_zero
55
  
56
  integer, parameter :: collision_model_lorentz = 1      ! if this changes, check gs2_diagnostics
57
  integer, parameter :: collision_model_none = 3
58
  integer, parameter :: collision_model_lorentz_test = 5 ! if this changes, check gs2_diagnostics
59
  integer, parameter :: collision_model_full = 6
60
  integer, parameter :: collision_model_ediffuse = 7
61

62
  integer, parameter :: lorentz_scheme_default = 1
63
  integer, parameter :: lorentz_scheme_old = 2
64

65
  integer, parameter :: ediff_scheme_default = 1
66
  integer, parameter :: ediff_scheme_old = 2
67

68
  real, dimension (2) :: vnmult = -1.0
69
  integer :: ncheck
70
  logical :: vary_vnew
71
  real :: vnfac, vnslow
72
  real :: etol, ewindow, etola, ewindowa
73
  integer :: timesteps_between_collisions
74
  logical :: force_collisions, has_lorentz, has_diffuse, skip_parallel_boundary
75
  real, dimension (2) :: vnm_init = 1.0
76

77
  ! collisional diagnostic of heating rate
78
  complex, dimension (:,:,:,:,:), allocatable :: c_rate
79
  ! (-ntgrid:ntgrid,ntheta0,naky,nspecies,2) replicated
80

81
  real, dimension (:,:,:), allocatable :: dtot
82
  ! (-ntgrid:ntgrid,nlambda,max(ng2,nlambda-ng2)) lagrange coefficients for derivative error estimate
83

84
  real, dimension (:,:), allocatable :: fdf, fdb
85
  ! (-ntgrid:ntgrid,nlambda) finite difference coefficients for derivative error estimate
86

87
  real, dimension (:,:,:), allocatable :: vnew, vnew_s, vnew_D, vnew_E, delvnew
88
  ! (naky,negrid,nspec) replicated
89

90
  real, dimension (:,:), allocatable :: vpdiffle
91
  real, dimension (:,:,:), allocatable :: vpdiff
92
  ! (-ntgrid:ntgrid,2,nlambda) replicated
93

94
  ! only for hyper-diffusive collisions
95
  real, dimension (:,:,:,:), allocatable :: vnewh
96
  ! (-ntgrid:ntgrid,ntheta0,naky,nspec) replicated
97

98
  ! only for momentum conservation due to Lorentz operator (8.06)
99
  real, dimension(:,:,:), allocatable :: s0, w0, z0
100
  ! The following (between the start and finish comments) are only used for LE layout
101
  ! start
102
  real, dimension (:,:,:), allocatable :: s0le, w0le, z0le
103
  ! Copies of aj0, aj1 and vpa from dist_fn_arrays stored in
104
  ! le layout shaped arrays.
105
  real, dimension (:,:,:), allocatable :: aj0le, vperp_aj1le, vpa_aj0_le
106
  ! finish
107

108
  ! needed for momentum and energy conservation due to energy diffusion (3.08)
109
  real, dimension(:,:,:), allocatable :: bs0, bw0, bz0
110

111
  ! The following (between the start and finish comments) are only used for LE layout
112
  ! start
113
  real, dimension (:,:,:), allocatable :: bs0le, bw0le, bz0le
114
  ! finish
115

116
  real :: cfac
117
  integer :: nxi_lim !< Sets the upper xi index which we consider in loops
118
  ! The following (between the start and finish comments) are only used for LE layout
119
  ! start
120
  ! only for lorentz
121
  real, dimension (:,:,:), allocatable :: c1le, betaale, qle, d1le, h1le
122
  ! only for energy diffusion
123
  real, dimension (:,:,:), allocatable :: ec1le, ebetaale, eqle
124
  ! finish
125
  ! The following (between the start and finish comments) are only used for none LE layout
126
  ! start
127
  ! only for lorentz
128
  real, dimension (:,:), allocatable :: c1, betaa, ql, d1, h1
129
  ! only for energy diffusion
130
  real, dimension (:,:), allocatable :: ec1, ebetaa, eql
131
  ! finish
132

133
  real, dimension (:, :), allocatable :: pitch_weights
134

135
  logical :: drag = .false.
136
  logical :: colls = .true.
137
  logical :: split_collisions
138

139
  logical :: hypermult
140
  logical :: initialized = .false.
141

142
  !> Used to represent the input configuration of collisions
143
  type, extends(abstract_config_type) :: collisions_config_type
144
     ! namelist : collisions_knobs
145
     ! indexed : false
146
     !> If true (default) then transform from the gyro-averaged
147
     !> distribution function \(g\) evolved by GS2 to the
148
     !> non-Boltzmann part of \(\delta f\), (\(h\)), when applying the
149
     !> collision operator. This is the physically appropriate choice,
150
     !> this parameter is primarily for numerical testing.
151
     logical :: adjust = .true.
152
     !> Factor multipyling the finite Larmor radius terms in the
153
     !> collision operator. This term is essentially just classical
154
     !> diffusion. Set `cfac` to 0 to turn off this diffusion.
155
     !>
156
     !> @note Default changed to 1.0 in order to include classical
157
     !> diffusion April 18 2006
158
     real :: cfac = 1.0
159
     !> Selects the collision model used in the simulation. Can be one
160
     !> of
161
     !>
162
     !> - `'default'` : Include both pitch angle scattering and energy
163
     !> diffusion.
164
     !> - `'collisionless'` : Disable the collision operator.
165
     !> - `'none'` : Equivalent to `'collisionless'`.
166
     !> - `'lorentz'` : Only pitch angle scattering.
167
     !> - `'lorentz-test'` : Only pitch angle scattering. For testing,
168
     !> disables some components of the operator.
169
     !> - `'ediffuse'` : Only energy diffusion.
170
     !>
171
     !> If no species have a non-zero collision frequency, `vnewk`, then
172
     !> the collision operator is also automatically disabled.
173
     character(len = 20) :: collision_model = 'default'
174
     !> If true (default) then guarantee exact conservation
175
     !> properties.
176
     logical :: conservative = .true.
177
     !> If true (default) then forces conservation corrections to zero
178
     !> in the forbidden region to avoid introducing unphysical
179
     !> non-zero values for the distribution function in the forbidden
180
     !> region of trapped particles.
181
     !>
182
     !> @todo Confirm above documentation is accurate.
183
     !>
184
     !> @note The conserving terms calculated as part of the field
185
     !> particle collision operator should respect the forbidden of
186
     !> the distribution function for trapped particles. This is a
187
     !> cosmetic change, but has the result that plots of the
188
     !> distribution function for trapped particles makes sense. Note
189
     !> that terms involving vpa do not need to be modified Because
190
     !> vpa = 0 in the forbidden region because of this explicit
191
     !> forbid statements have not been added to the drag term
192
     !> involving apar.
193
     logical :: conserve_forbid_zero = .true.
194
     !> If true (default) then guarantee collision operator conserves
195
     !> momentum and energy.
196
     !>
197
     !> @todo Clarify the difference with [[collisions_knobs:conservative]].
198
     !>
199
     !> @note Default changed to reflect improved momentum and energy
200
     !> conversation 07/08
201
     logical :: conserve_moments = .true.  
202
     !> If true (not the default) then use the thermal velocity to
203
     !> evaluate the collision frequencies to be used. This results in
204
     !> an energy independent collision frequency being used for all
205
     !> species.
206
     logical :: const_v = .false.
207
     !> Controls how the coefficients in the matrix representing the
208
     !> energy diffusion operator are obtained. Can be one of
209
     !>
210
     !> - `'default'` : Use a conservative scheme.
211
     !> - `'old'` : Use the original non-conservative scheme.
212
     !>
213
     !> @todo Consider deprecating/removing the `'old'` option.
214
     character(len = 20) :: ediff_scheme = 'default'
215
     !> If true (not the default) then force the collision frequency
216
     !> used for all non-electron species to zero and force all
217
     !> electron-electron terms to zero.
218
     logical :: ei_coll_only = .false.
219
     !> Only used in [[get_verr]] as a part of the adaptive
220
     !> collisionality algorithm. Sets the maximum relative error
221
     !> allowed, above which the collision frequency must be
222
     !> increased.
223
     !>
224
     !> @todo Confirm this is really to set the relative error limit.
225
     real :: etol = 2.e-2
226
     !> Only used in [[get_verr]] as a part of the adaptive
227
     !> collisionality algorithm. Sets the maximum absolute error
228
     !> allowed, above which the collision frequency must be
229
     !> increased.
230
     !>
231
     !> @todo Confirm this is really to set the absolute error limit.     
232
     real :: etola = 2.e-2
233
     !> Only used in [[get_verr]] as a part of the adaptive
234
     !> collisionality algorithm. Sets an offset to apply to the
235
     !> relative error limit set by [[collisions_knobs:etol]]. This is used to provide
236
     !> hysteresis is the adaptive collisionality algorithm so to
237
     !> avoid adjusting the collision frequency up and down every step
238
     !> (similar to [[reinit_knobs:delt_cushion]]).
239
     !>
240
     !> @todo Confirm the above description.
241
     real :: ewindow = 1.e-2
242
     !> Only used in [[get_verr]] as a part of the adaptive
243
     !> collisionality algorithm. Sets an offset to apply to the
244
     !> absolute error limit set by [[collisions_knobs:etola]]. This is used to provide
245
     !> hysteresis is the adaptive collisionality algorithm so to
246
     !> avoid adjusting the collision frequency up and down every step
247
     !> (similar to the [[reinit_knobs:delt_cushion]]).
248
     !>
249
     !> @todo Confirm the above description.     
250
     real :: ewindowa = 1.e-2
251
     !> Currently we skip the application of the selected collision
252
     !> operator for species with zero collision frequency and disable
253
     !> collisions entirely if no species have non-zero collision
254
     !> frequency. This is generally sensible, but it can be useful to
255
     !> force the use of the collisional code path in some situations
256
     !> such as in code testing. Setting this flag to `.true.` forces
257
     !> the selected collision model operator to be applied even if
258
     !> the collision frequency is zero.
259
     logical :: force_collisions = .false.
260
     !> If true (not the default) then calculate collisional heating
261
     !> when applying the collion operator. This is purely a
262
     !> diagnostic calculation. It should not change the evolution.
263
     !>
264
     !> @todo : Verify this does not influence the evolution.
265
     logical :: heating = .false.
266
     !> If true (not the default) then multiply the hyper collision
267
     !> frequency by the species' collision frequency
268
     !> [[species_parameters:nu_h]]. This only impacts the pitch
269
     !> angle scattering operator.
270
     !>
271
     !> @note The hyper collision frequency is only non-zero if any
272
     !> species have a non-zero `nu_h` value set in the input. If any
273
     !> are set then the hyper collision frequency is simply `nu_h *
274
     !> kperp2 * kperp2` (where `kperp2` here is normalised to the
275
     !> maximum `kperp2`).
276
     logical :: hypermult = .false.
277
     !> Controls how the coefficients in the matrix representing the
278
     !> pitch angle scattering operator are obtained. Can be one of
279
     !>
280
     !> - `'default'` : Use a conservative scheme.
281
     !> - `'old'` : Use the original non-conservative scheme.
282
     !>
283
     !> @todo Consider deprecating/removing the `'old'` option.
284
     character(len = 20) :: lorentz_scheme = 'default'
285
     !> Used as a part of the adaptive collisionality algorithm. When
286
     !> active we check the velocity space error with [[get_verr]]
287
     !> every `ncheck` time steps. This check can be relatively
288
     !> expensive so it is recommended to avoid small values of
289
     !> `ncheck`.
290
     !>
291
     !> @warning The new diagnostics module currently ignores this
292
     !> value and instead uses its own input variable named `ncheck`
293
     !> (which has a different default). See [this
294
     !> bug](https://bitbucket.org/gyrokinetics/gs2/issues/88).
295
     integer :: ncheck = 100
296
     !> If true (default) then potentially include the drag term in
297
     !> the pitch angle scattering operator. This is a necessary but
298
     !> not sufficient criteria. For the drag term to be included we
299
     !> also require \(\beta\neq 0\), more than one simulated species
300
     !> and finite \(A_\|\) perturbations included in the simulation
301
     !> (i.e. `fapar /= 0`).
302
     logical :: resistivity = .true.
303
     !> If true then we avoid applying collisions at the ends of the
304
     !> parallel domain. This is _not_ the correct treatment but is
305
     !> rather an experimental flag to allow testing whilst a correct
306
     !> treatment for collisions at this point is developed.  See
307
     !> [issue:192](https://bitbucket.org/gyrokinetics/gs2/issues/192/collisions-can-violate-parallel-boundary)
308
     !> for more details. This can avoid numerical instability observed
309
     !> in simulations including A||.
310
     logical :: skip_parallel_boundary = .false.
311
     !> If true (not the default) then use special handling for the
312
     !> wfb particle in the pitch angle scattering operator.
313
     !>
314
     !> @note MRH changed default 16/08/2018. Previous default of true
315
     !> seemed to cause a numerical issue in flux tube simulations for
316
     !> the zonal modes at large \(k_x\).
317
     !>
318
     !> @todo Improve this documentation
319
     logical :: special_wfb_lorentz = .false.
320
     !> If true (not the default) then remove the collision operator
321
     !> from the usual time advance algorithm. Instead the collision
322
     !> operator is only applied every
323
     !> [[collisions_knobs:timesteps_between_collisions]]
324
     !> timesteps. This can potentially substantially speed up
325
     !> collisional simulations, both in the initialisation and
326
     !> advance phases.
327
     !>
328
     !> @warning Currently the input
329
     !> [[collisions_knobs:timesteps_between_collisions]] is ignored
330
     !> so collisions are applied every time step. The primary result
331
     !> of `split_collision = .true.` currently is that collisions are
332
     !> not applied in the first linear solve used as a part of a
333
     !> single time step. Hence the cost of collisions in advance are
334
     !> roughly halved. The full saving in the initialisation phase is
335
     !> still realised.
336
     logical :: split_collisions = .false.
337
     !> If true (not the default) then performs some additional checks
338
     !> of the data redistribution routines associated with
339
     !> transforming being the standard and collisional data
340
     !> decompositions.
341
     logical :: test = .false.
342
     !> Should set the number of timesteps between application of the
343
     !> collision operator if [[collisions_knobs:split_collisions]] is
344
     !> true. Currently this is ignored.
345
     !>
346
     !> @warning This value is currently ignored.
347
     integer :: timesteps_between_collisions = 1
348
     !> If true (default) then use a data decomposition for collisions
349
     !> that brings both pitch angle and energy local to a
350
     !> processor. This is typically the most efficient option,
351
     !> however for collisional simulations that only use part of the
352
     !> collision operator (either energy diffusion or pitch angle
353
     !> scattering) then it may be beneficial to set this flag to
354
     !> false such that we use a decomposition that only brings either
355
     !> energy or pitch angle local.
356
     logical :: use_le_layout = .true.
357
     !> Set to true (not the default) to activate the adaptive
358
     !> collisionality algorithm.
359
     !>
360
     !> @todo Provide more documentation on the adaptive
361
     !> collisionality algorithm.
362
     logical :: vary_vnew = .false.
363
     !> If the collisionality is to be increased as a part of the
364
     !> adaptive collisionality algorithm then increase it by this
365
     !> factor.
366
     real :: vnfac = 1.1
367
     !> If the collisionality is to be decreased as a part of the
368
     !> adaptive collisionality algorithm then decrease it by this
369
     !> factor.
370
     real :: vnslow = 0.9
371
     !> Controls how the duplicate `vpar = 0` point is handled.  When
372
     !> `vpar_zero_mean = .true.` (the default) the average of `g(vpar
373
     !> = 0)` for both signs of the parallel velcoity (`isgn`) is used
374
     !> in the collision operator instead of just `g(vpar = 0)` at
375
     !> `isgn=2`.  This is seen to suppress a numerical instability
376
     !> when `special_wfb_lorentz =.false.`. With these defaults pitch
377
     !> angle scattering at \(\theta = \pm \pi \) is now being handled
378
     !> physically i.e. `vpar = 0` at this theta location is no longer
379
     !> being skipped.
380
     !>
381
     !> @todo Consider removing this option.
382
     logical :: vpar_zero_mean = .true.
383
#include "collisions_overrides_and_bound_auto_gen.inc"
384
  end type collisions_config_type
385

386
  type(collisions_config_type) :: collisions_config
387
contains
388

389
  subroutine set_vnmult(vnmult_in)
×
390
    real, dimension(2), intent(in) :: vnmult_in
391
    vnmult = vnmult_in
×
392
  end subroutine set_vnmult
×
393

394
  subroutine set_heating(heating_in)
×
395
    logical, intent(in) :: heating_in
396
    heating = heating_in
×
397
  end subroutine set_heating
×
398
  
399
  !> FIXME : Add documentation
400
  subroutine check_collisions(report_unit)
×
401
    use warning_helpers, only: is_zero
402
    implicit none
403
    integer, intent(in) :: report_unit
404
    select case (collision_model_switch)
×
405
    case (collision_model_lorentz,collision_model_lorentz_test)
406
       write (report_unit, fmt="('A Lorentz collision operator has been selected.')")
×
407
       if (cfac > 0) write (report_unit, fmt="('This has both terms of the Lorentz collision operator: cfac=',e12.4)") cfac
×
408
       if (is_zero(cfac)) write (report_unit, fmt="('This is only a partial Lorentz collision operator (cfac=0.0)')")
×
409
       if (const_v) write (report_unit, fmt="('This is an energy independent Lorentz collision operator (const_v=true)')")  
×
410
!          if (hypercoll) call init_hyper_lorentz
411
    case (collision_model_full)
412
       write (report_unit, fmt="('Full GS2 collision operator has been selected.')")
×
413
    end select
414
  end subroutine check_collisions
×
415

416
  !> FIXME : Add documentation
417
  subroutine wnml_collisions(unit)
×
418
    implicit none
419
    integer, intent(in) :: unit
420
    call collisions_config%write(unit)
×
421
  end subroutine wnml_collisions
×
422

423
  !> FIXME : Add documentation
424
  subroutine init_collisions(collisions_config_in)
×
425
    use species, only: spec
426
    implicit none
427
    type(collisions_config_type), intent(in), optional :: collisions_config_in
428
    if (initialized) return
×
429
    initialized = .true.
×
430

431
    hyper_colls = .false.
×
432
    if (any(spec%nu_h > epsilon(0.0))) hyper_colls = .true.
×
433
    call read_parameters(collisions_config_in)
×
434

435
    call init_arrays
×
436

437
  end subroutine init_collisions
438

439
  !> FIXME : Add documentation
440
  subroutine read_parameters(collisions_config_in)
×
441
    use file_utils, only: error_unit
442
    use text_options, only: text_option, get_option_value
443
    use run_parameters, only: beta, has_apar
444
    use species, only: nspec
445
    use le_grids, only: nxi, ng2
446
    implicit none
447
    type(collisions_config_type), intent(in), optional :: collisions_config_in
448
    
449
    type (text_option), dimension (6), parameter :: modelopts = &
450
         [ text_option('default', collision_model_full), &
451
            text_option('lorentz', collision_model_lorentz), &
452
            text_option('ediffuse', collision_model_ediffuse), &
453
            text_option('lorentz-test', collision_model_lorentz_test), &
454
            text_option('none', collision_model_none), &
455
            text_option('collisionless', collision_model_none) ]
456
    type (text_option), dimension (2), parameter :: schemeopts = &
457
         [ text_option('default', lorentz_scheme_default), &
458
            text_option('old', lorentz_scheme_old) ]
459
    type (text_option), dimension (2), parameter :: eschemeopts = &
460
         [ text_option('default', ediff_scheme_default), &
461
            text_option('old', ediff_scheme_old) ]
462
    character(20) :: collision_model, lorentz_scheme, ediff_scheme
463
    integer :: ierr
464

465
    if (present(collisions_config_in)) collisions_config = collisions_config_in
×
466

467
    call collisions_config%init(name = 'collisions_knobs', requires_index = .false.)
×
468

469
    ! Copy out internal values into module level parameters
470
    associate(self => collisions_config)
471
#include "collisions_copy_out_auto_gen.inc"
472
    end associate
473

474
    ierr = error_unit()
×
475
    call get_option_value &
476
         (collision_model, modelopts, collision_model_switch, &
477
         ierr, "collision_model in collisions_knobs",.true.)
×
478

479
    call get_option_value &
480
         (lorentz_scheme, schemeopts, lorentz_switch, &
481
         ierr, "lorentz_scheme in collisions_knobs",.true.)
×
482
    
483
    call get_option_value &
484
         (ediff_scheme, eschemeopts, ediff_switch, &
485
         ierr, "ediff_scheme in collisions_knobs",.true.)
×
486

487
    select case (collision_model_switch)
×
488
    case (collision_model_full)
489
       has_lorentz = .true.  ; has_diffuse = .true.
×
490
    case (collision_model_lorentz,collision_model_lorentz_test)
491
       has_lorentz = .true.  ; has_diffuse = .false.
×
492
    case (collision_model_ediffuse)
493
       has_lorentz = .false. ; has_diffuse = .true.
×
494
    case default
495
       has_lorentz = .false. ; has_diffuse = .false.
×
496
    end select
497

498
    drag = has_lorentz .and. resistivity .and. (beta > epsilon(0.0)) &
499
         .and. (nspec > 1) .and. has_apar
×
500

501
    ! The nxi > 2 * ng2 check appears to be checking if we have
502
    ! trapped particles or not so could be replaced with grid_has_trapped_particles()
503
    if (nxi > 2 * ng2 ) then
×
504
       nxi_lim = nxi + 1
×
505
    else
506
       nxi_lim = nxi
×
507
    end if
508
  end subroutine read_parameters
×
509

510
  !> A wrapper to sqrt which replaces -ve values with 0.0 to avoid
511
  !> NaNs arising from slight floating point discrepancies. We could
512
  !> consider adding a debug check to abort/warn if the passed value
513
  !> is too negative (i.e. if it looks like an error rather than small
514
  !> round off).
515
  elemental real function safe_sqrt(arg)
×
516
    implicit none
517
    real, intent(in) :: arg
518
    safe_sqrt = sqrt(max(0.0, arg))
×
519
  end function safe_sqrt
×
520

521
  !> FIXME : Add documentation
522
  subroutine init_arrays
×
523
    use species, only: nspec
524
    use le_grids, only: init_map
525
    use kt_grids, only: naky, ntheta0
526
    use theta_grid, only: ntgrid
527
    use array_utils, only: zero_array
528
    implicit none
529
    logical :: use_lz_layout, use_e_layout
530

531
    use_lz_layout = .false. ; use_e_layout = .false.
×
532

533
    if (collision_model_switch == collision_model_none) then
×
534
       colls = .false.
×
535
       return
×
536
    end if
537

538
    call init_vnew
×
539
    if (all(abs(vnew(:,1,:)) <= 2.0*epsilon(0.0)) .and. .not. force_collisions) then
×
540
       collision_model_switch = collision_model_none
×
541
       colls = .false.
×
542
       return
×
543
    end if
544

545
    if (heating .and. .not. allocated(c_rate)) then
×
546
       allocate (c_rate(-ntgrid:ntgrid, ntheta0, naky, nspec, 3))
×
547
       call zero_array(c_rate)
×
548
    end if
549

550
    use_lz_layout = has_lorentz .and. .not. use_le_layout
×
551
    use_e_layout = has_diffuse .and. .not. use_le_layout
×
552
    call init_map (use_lz_layout, use_e_layout, use_le_layout, test)
×
553

554
    if (has_lorentz) then
×
555
       call init_lorentz
×
556
       if (conserve_moments) call init_lorentz_conserve
×
557
    end if
558

559
    if (has_diffuse) then
×
560
       call init_ediffuse
×
561
       if (conserve_moments) call init_diffuse_conserve
×
562
    end if
563

564
    if (use_le_layout .and. (conserve_moments .or. drag)) call init_le_bessel
×
565
  end subroutine init_arrays
566

567
  !> Communicate Bessel functions from g_lo to le_lo
568
  subroutine init_le_bessel
×
569
    use gs2_layouts, only: g_lo, le_lo, ig_idx
570
    use dist_fn_arrays, only: aj0, aj1, vpa
571
    use le_grids, only: negrid, g2le, ixi_to_il, energy => energy_maxw, al
572
    use theta_grid, only: ntgrid
573
    use redistribute, only: gather, scatter
574
    use array_utils, only: zero_array
575
    implicit none
576
    complex, dimension (:,:,:), allocatable :: ctmp, z_big
×
577
    integer :: ile, ig, ie, ixi, il
578

579
    allocate (z_big(-ntgrid:ntgrid, 2, g_lo%llim_proc:g_lo%ulim_alloc))
×
580
    allocate (ctmp(nxi_lim, negrid+1, le_lo%llim_proc:le_lo%ulim_alloc))
×
581
    ! We need to initialise ctmp as it is used as receiving buffer in
582
    ! g2le redistribute, which doesn't populate all elements
583
    call zero_array(ctmp)
×
584

585
    ! next set aj0le & aj1l
586
    z_big(:,1,:) = cmplx(aj0,aj1)
×
587
    z_big(:,2,:) = z_big(:,1,:)
×
588

589
    call gather (g2le, z_big, ctmp)
×
590

591
    if (.not. allocated(aj0le)) then
×
592
       allocate (aj0le(nxi_lim, negrid+1, le_lo%llim_proc:le_lo%ulim_alloc))
×
593
       allocate (vperp_aj1le(nxi_lim, negrid+1, le_lo%llim_proc:le_lo%ulim_alloc))
×
594
    end if
595

596
    aj0le = real(ctmp)
×
597
    vperp_aj1le = aimag(ctmp) !< Currently just aj1
×
598

599
    !$OMP PARALLEL DO DEFAULT(none) &
600
    !$OMP PRIVATE(ile, ig, ixi, ie, il) &
601
    !$OMP SHARED(le_lo, negrid, nxi_lim, ixi_to_il, vperp_aj1le, energy, al) &
602
    !$OMP SCHEDULE(static)
603
    do ile = le_lo%llim_proc, le_lo%ulim_proc
×
604
       ig = ig_idx(le_lo, ile)
×
605
       do ie = 1, negrid
×
606
          do ixi = 1, nxi_lim
×
607
             il = ixi_to_il(ixi, ig)
×
608
             vperp_aj1le(ixi, ie, ile) = vperp_aj1le(ixi, ie, ile) * energy(ie) * al(il)
×
609
          end do
610
       end do
611
    end do
612
    !$OMP END PARALLEL DO
613

614
    z_big = vpa
×
615
    call gather (g2le, z_big, ctmp)
×
616
    deallocate(z_big)
×
617
    if (.not. allocated(vpa_aj0_le)) then
×
618
       allocate (vpa_aj0_le(nxi_lim, negrid+1, le_lo%llim_proc:le_lo%ulim_alloc))
×
619
    end if
620
    vpa_aj0_le = real(ctmp) * aj0le
×
621
  end subroutine init_le_bessel
×
622

623
  !> Precompute three quantities needed for momentum and energy conservation:
624
  !> z0, w0, s0 (z0le, w0le, s0le if use_le_layout chosen in the input file defined)
625
  subroutine init_lorentz_conserve
×
626
    use gs2_layouts, only: g_lo, ie_idx, is_idx, ik_idx, il_idx, it_idx
627
    use species, only: nspec, spec, is_electron_species
628
    use kt_grids, only: kperp2, naky, ntheta0
629
    use theta_grid, only: ntgrid, bmag
630
    use le_grids, only: energy => energy_maxw, speed => speed_maxw, al, &
631
         integrate_moment, negrid, forbid
632
    use gs2_time, only: code_dt, tunits
633
    use dist_fn_arrays, only: aj0, aj1, vpa
634
    use le_grids, only: g2le
635
    use gs2_layouts, only: le_lo
636
    use redistribute, only: gather, scatter
637
    use array_utils, only: zero_array
638
    implicit none
639
    real, dimension (:,:,:), allocatable :: gtmp
×
640
    real, dimension (:,:,:,:), allocatable :: duinv, dtmp, vns
×
641
    integer :: ie, il, ik, is, isgn, iglo,  it, ig
642
    complex, dimension (:,:,:), allocatable :: ctmp, z_big
×
643
    complex, dimension(:,:,:), allocatable :: s0tmp, w0tmp, z0tmp
×
644
    logical, parameter :: all_procs = .true.
645

646
    if(use_le_layout) then
×
647
       allocate (ctmp(nxi_lim, negrid+1, le_lo%llim_proc:le_lo%ulim_alloc))
×
648
       ! We need to initialise ctmp as it is used as receiving buffer in
649
       ! g2le redistribute, which doesn't populate all elements
650
       call zero_array(ctmp)
×
651
    end if
652

653
    allocate(s0tmp(-ntgrid:ntgrid,2,g_lo%llim_proc:g_lo%ulim_alloc))
×
654
    allocate(w0tmp(-ntgrid:ntgrid,2,g_lo%llim_proc:g_lo%ulim_alloc))
×
655
    allocate(z0tmp(-ntgrid:ntgrid,2,g_lo%llim_proc:g_lo%ulim_alloc))
×
656

657
    allocate (gtmp(-ntgrid:ntgrid,2,g_lo%llim_proc:g_lo%ulim_alloc))
×
658
    allocate (duinv(-ntgrid:ntgrid, ntheta0, naky, nspec))
×
659
    allocate (dtmp(-ntgrid:ntgrid, ntheta0, naky, nspec))
×
660
    allocate (vns(naky,negrid,nspec,3))
×
661

662
    call zero_array(duinv)
×
663
    call zero_array(dtmp)
×
664

665
    vns(:,:,:,1) = vnmult(1)*vnew_D
×
666
    vns(:,:,:,2) = vnmult(1)*vnew_s
×
667
    vns(:,:,:,3) = 0.0
×
668

669
    if (drag) then
×
670
       do is = 1, nspec
×
671
          if (.not. is_electron_species(spec(is))) cycle
×
672
          do ik = 1, naky
×
673
             vns(ik,:,is,3) = vnmult(1)*spec(is)%vnewk*tunits(ik)/energy**1.5
×
674
          end do
675
       end do
676

677
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
678
! Now get z0 (first form)
679
       !$OMP PARALLEL DO DEFAULT(none) &
680
       !$OMP PRIVATE(iglo, ik, ie, il, is, isgn) &
681
       !$OMP SHARED(g_lo, conservative, z0tmp, code_dt, vns, vpdiff, speed, aj0, vpa) &
682
       !$OMP SCHEDULE(static)
683
       do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
684
          ik = ik_idx(g_lo,iglo)
×
685
          ie = ie_idx(g_lo,iglo)
×
686
          il = il_idx(g_lo,iglo)
×
687
          is = is_idx(g_lo,iglo)
×
688
          do isgn = 1, 2
×
689
             ! u0 = -2 nu_D^{ei} vpa J0 dt f0
690
             if (conservative) then
×
691
                z0tmp(:,isgn,iglo) = -2.0*code_dt*vns(ik,ie,is,3)*vpdiff(:,isgn,il) &
×
692
                     * speed(ie)*aj0(:,iglo)
×
693
             else
694
                z0tmp(:,isgn,iglo) = -2.0*code_dt*vns(ik,ie,is,3)*vpa(:,isgn,iglo)*aj0(:,iglo)
×
695
             end if
696
          end do
697
       end do
698
       !$OMP END PARALLEL DO
699

700
       call zero_out_passing_hybrid_electrons(z0tmp)
×
701

702
       if(use_le_layout) then
×
703
          call gather (g2le, z0tmp, ctmp)
×
704
          call solfp_lorentz_le_layout (ctmp)
×
705
          call scatter (g2le, ctmp, z0tmp)   ! z0 is redefined below
×
706
       else
707
          call solfp_lorentz_standard_layout (z0tmp)   ! z0 is redefined below
×
708
       end if
709

710
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
711
! Now get v0z0
712

713
       !$OMP PARALLEL DO DEFAULT(none) &
714
       !$OMP PRIVATE(iglo, isgn) &
715
       !$OMP SHARED(g_lo, vpa, gtmp, aj0, z0tmp) &
716
       !$OMP COLLAPSE(2) &
717
       !$OMP SCHEDULE(static)
718
       do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
719
          do isgn = 1, 2
×
720
             ! v0 = vpa J0 f0
721
             gtmp(:,isgn,iglo) = vpa(:,isgn,iglo) * aj0(:,iglo) * real(z0tmp(:,isgn,iglo))
×
722
          end do
723
       end do
724
       !$OMP END PARALLEL DO
725

726
       call integrate_moment (gtmp, dtmp, all_procs) ! v0z0
×
727

728
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
729
! Redefine z0 = z0 / (1 + v0z0)
730

731
       !$OMP PARALLEL DO DEFAULT(none) &
732
       !$OMP PRIVATE(iglo, ik, it, is, isgn) &
733
       !$OMP SHARED(g_lo, z0tmp, dtmp) &
734
       !$OMP SCHEDULE(static)
735
       do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
736
          it = it_idx(g_lo,iglo)
×
737
          ik = ik_idx(g_lo,iglo)
×
738
          is = is_idx(g_lo,iglo)
×
739
          do isgn = 1, 2
×
740
             z0tmp(:,isgn,iglo) = z0tmp(:,isgn,iglo) / (1.0 + dtmp(:,it,ik,is))
×
741
          end do
742
       end do
743
       !$OMP END PARALLEL DO
744

745
    else
746
       !If drag is false vns(...,3) is zero and hence z0tmp is zero here.
747
       call zero_array(z0tmp)
×
748
    end if
749

750
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
751

752
    ! du == int (E nu_s f_0);  du = du(z, kx, ky, s)
753
    ! duinv = 1/du
754
    if (conservative) then
×
755
       !$OMP PARALLEL DO DEFAULT(none) &
756
       !$OMP PRIVATE(iglo, ik, ie, il, is, isgn) &
757
       !$OMP SHARED(g_lo, gtmp, vns, vpa, vpdiff, speed) &
758
       !$OMP SCHEDULE(static)
759
       do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
760
          ik = ik_idx(g_lo,iglo)
×
761
          ie = ie_idx(g_lo,iglo)
×
762
          il = il_idx(g_lo,iglo)
×
763
          is = is_idx(g_lo,iglo)
×
764
          do isgn = 1, 2
×
765
             gtmp(:,isgn,iglo)  = vns(ik,ie,is,1)*vpa(:,isgn,iglo) &
×
766
                  * vpdiff(:,isgn,il)*speed(ie)
×
767
          end do
768
       end do
769
       !$OMP END PARALLEL DO
770
    else
771
       !$OMP PARALLEL DO DEFAULT(none) &
772
       !$OMP PRIVATE(iglo, ik, ie, is, isgn) &
773
       !$OMP SHARED(g_lo, gtmp, vpa, vns) &
774
       !$OMP SCHEDULE(static)
775
       do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
776
          ik = ik_idx(g_lo,iglo)
×
777
          ie = ie_idx(g_lo,iglo)
×
778
          is = is_idx(g_lo,iglo)
×
779
          do isgn = 1, 2
×
780
             gtmp(:,isgn,iglo)  = vns(ik,ie,is,1)*vpa(:,isgn,iglo)**2
×
781
          end do
782
       end do
783
       !$OMP END PARALLEL DO
784
    end if
785

786
    call integrate_moment (gtmp, duinv, all_procs)  ! not 1/du yet
×
787

788
    ! Could replace this with OpenMP using an explicit loop. TAG
789
    where (abs(duinv) > epsilon(0.0))  ! necessary b/c some species may have vnewk=0
×
790
       !duinv=0 iff vnew=0 so ok to keep duinv=0.
791
       duinv = 1./duinv  ! now it is 1/du
×
792
    end where
793

794
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
795
! Now get s0 (first form)
796
    !$OMP PARALLEL DO DEFAULT(none) &
797
    !$OMP PRIVATE(iglo, ik, it, ie, il, is, isgn, ig) &
798
    !$OMP SHARED(g_lo, conservative, s0tmp, vns, vpdiff, speed, aj0, code_dt, duinv, vpa) &
799
    !$OMP SCHEDULE(static)
800
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
801
       it = it_idx(g_lo,iglo)
×
802
       ik = ik_idx(g_lo,iglo)
×
803
       ie = ie_idx(g_lo,iglo)
×
804
       il = il_idx(g_lo,iglo)
×
805
       is = is_idx(g_lo,iglo)
×
806
       do isgn = 1, 2
×
807
          ! u1 = -3 nu_s vpa dt J0 f_0 / du
808
          if (conservative) then
×
809
             s0tmp(:,isgn,iglo) = -vns(ik,ie,is,1)*vpdiff(:,isgn,il)*speed(ie) &
×
810
                  * aj0(:,iglo)*code_dt*duinv(:,it,ik,is)
×
811
          else
812
             s0tmp(:,isgn,iglo) = -vns(ik,ie,is,1)*vpa(:,isgn,iglo) &
×
813
                  * aj0(:,iglo)*code_dt*duinv(:,it,ik,is)
×
814
          end if
815
       end do
816
    end do
817
    !$OMP END PARALLEL DO
818

819
    call zero_out_passing_hybrid_electrons(s0tmp)
×
820

821
    if(use_le_layout) then
×
822
       call gather (g2le, s0tmp, ctmp)
×
823
       call solfp_lorentz_le_layout (ctmp)
×
824
       call scatter (g2le, ctmp, s0tmp)   ! s0
×
825
    else
826
       call solfp_lorentz_standard_layout (s0tmp)   ! s0
×
827
    end if
828

829
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
830
! Now get v0s0
831

832
    !$OMP PARALLEL DO DEFAULT(none) &
833
    !$OMP PRIVATE(iglo, isgn) &
834
    !$OMP SHARED(g_lo, gtmp, vpa, aj0, s0tmp) &
835
    !$OMP COLLAPSE(2) &
836
    !$OMP SCHEDULE(static)
837
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
838
       do isgn = 1, 2
×
839
          ! v0 = vpa J0 f0
840
          gtmp(:,isgn,iglo) = vpa(:,isgn,iglo) * aj0(:,iglo) * real(s0tmp(:,isgn,iglo))
×
841
       end do
842
    end do
843
    !OMP END PARALLEL DO
844

845
    call integrate_moment (gtmp, dtmp, all_procs)    ! v0s0
×
846

847
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
848
! Redefine s0 = s0 - v0s0 * z0 / (1 + v0z0)
849

850
    !$OMP PARALLEL DO DEFAULT(none) &
851
    !$OMP PRIVATE(iglo, ik, it, is, isgn) &
852
    !$OMP SHARED(g_lo, s0tmp, dtmp, z0tmp) &
853
    !$OMP SCHEDULE(static)
854
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
855
       ik = ik_idx(g_lo,iglo)
×
856
       it = it_idx(g_lo,iglo)
×
857
       is = is_idx(g_lo,iglo)
×
858
       do isgn=1,2
×
859
          s0tmp(:,isgn,iglo) = s0tmp(:,isgn,iglo) - dtmp(:,it,ik,is) * z0tmp(:,isgn,iglo)
×
860
       end do
861
    end do
862
    !$OMP END PARALLEL DO
863

864
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
865
! Now get v1s0
866

867
    !$OMP PARALLEL DO DEFAULT(none) &
868
    !$OMP PRIVATE(iglo, ik, ie, il, is, isgn) &
869
    !$OMP SHARED(g_lo, conservative, gtmp, vns, speed, vpdiff, aj0, s0tmp, vpa) &
870
    !$OMP SCHEDULE(static)
871
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
872
       ik = ik_idx(g_lo,iglo)
×
873
       ie = ie_idx(g_lo,iglo)
×
874
       il = il_idx(g_lo,iglo)
×
875
       is = is_idx(g_lo,iglo)
×
876
       do isgn = 1, 2
×
877
          ! v1 = nu_D vpa J0
878
          if (conservative) then
×
879
             gtmp(:,isgn,iglo) = vns(ik,ie,is,1)*speed(ie)*vpdiff(:,isgn,il) &
×
880
                  * aj0(:,iglo) * real(s0tmp(:,isgn,iglo))
×
881
          else
882
             gtmp(:,isgn,iglo) = vns(ik,ie,is,1)*vpa(:,isgn,iglo)*aj0(:,iglo) &
×
883
                  * real(s0tmp(:,isgn,iglo))
×
884
          end if
885
       end do
886
    end do
887
    !$OMP END PARALLEL DO
888

889
    call integrate_moment (gtmp, dtmp, all_procs)    ! v1s0
×
890

891
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
892
! Redefine s0 = s0 / (1 + v0s0)
893

894
    !$OMP PARALLEL DO DEFAULT(none) &
895
    !$OMP PRIVATE(iglo, ik, it, is, isgn) &
896
    !$OMP SHARED(g_lo, s0tmp, dtmp) &
897
    !$OMP SCHEDULE(static)
898
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
899
       ik = ik_idx(g_lo,iglo)
×
900
       it = it_idx(g_lo,iglo)
×
901
       is = is_idx(g_lo,iglo)
×
902
       do isgn=1,2
×
903
          s0tmp(:,isgn,iglo) = s0tmp(:,isgn,iglo) / (1.0 + dtmp(:,it,ik,is))
×
904
       end do
905
    end do
906
    !$OMP END PARALLEL DO
907

908
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
909
! Now get w0
910
    !$OMP PARALLEL DO DEFAULT(none) &
911
    !$OMP PRIVATE(iglo, ik, it, ie, il, is, isgn, ig) &
912
    !$OMP SHARED(g_lo, ntgrid, forbid, w0tmp, vns, energy, al , aj1, code_dt, &
913
    !$OMP spec, kperp2, duinv, bmag, conserve_forbid_zero) &
914
    !$OMP SCHEDULE(static)
915
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
916
       it = it_idx(g_lo,iglo)
×
917
       ik = ik_idx(g_lo,iglo)
×
918
       ie = ie_idx(g_lo,iglo)
×
919
       il = il_idx(g_lo,iglo)
×
920
       is = is_idx(g_lo,iglo)
×
921
       do isgn = 1, 2
×
922
          do ig=-ntgrid,ntgrid
×
923
             ! u2 = -3 dt J1 vperp vus a f0 / du
924
             if ( .not. (forbid(ig,il) .and. conserve_forbid_zero) ) then
×
925
                ! Note no conservative branch here, is that right?
926
                ! Note: energy * al * smz^2 * kperp2 / bmag is alpha^2 where
927
                ! alpha is the argument to the Bessel function, i.e. aj1 = J1(alpha) / alpha
928
                ! This appears to leave us with alpha * J1(alpha) whilst Barnes' paper
929
                ! only includes terms with J1(alpha). Note that alpha = vperp kperp smz/B
930
                ! so alpha * J1(alpha) = (kperp * smz / B) * vperp J1
931
                w0tmp(ig, isgn, iglo) = -vns(ik, ie, is, 1) * energy(ie) * al(il) &
×
932
                     * aj1(ig, iglo) * code_dt * spec(is)%smz**2 * kperp2(ig, it, ik) * &
×
933
                     duinv(ig, it, ik, is) / bmag(ig)
×
934
             else
935
                w0tmp(ig,isgn,iglo) = 0.
×
936
             endif
937
          end do
938
       end do
939
    end do
940
    !$OMP END PARALLEL DO
941

942
    call zero_out_passing_hybrid_electrons(w0tmp)
×
943

944
    if(use_le_layout) then
×
945
       call gather (g2le, w0tmp, ctmp)
×
946
       call solfp_lorentz_le_layout (ctmp)
×
947
       call scatter (g2le, ctmp, w0tmp)
×
948
    else
949
       call solfp_lorentz_standard_layout (w0tmp)
×
950
    end if
951

952
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
953
! Now get v0w0
954

955
    !$OMP PARALLEL DO DEFAULT(none) &
956
    !$OMP PRIVATE(iglo, isgn) &
957
    !$OMP SHARED(g_lo, gtmp, vpa, aj0, w0tmp) &
958
    !$OMP COLLAPSE(2) &
959
    !$OMP SCHEDULE(static)
960
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
961
       do isgn = 1, 2
×
962
          ! v0 = vpa J0 f0
963
          gtmp(:,isgn,iglo) = vpa(:,isgn,iglo) * aj0(:,iglo) * real(w0tmp(:,isgn,iglo))
×
964
       end do
965
    end do
966
    !$OMP END PARALLEL DO
967

968
    call integrate_moment (gtmp, dtmp, all_procs)    ! v0w0
×
969

970
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
971
! Redefine w0 = w0 - v0w0 * z0 / (1 + v0z0) (this is w1 from MAB notes)
972

973
    !$OMP PARALLEL DO DEFAULT(none) &
974
    !$OMP PRIVATE(iglo, ik, it, is, isgn) &
975
    !$OMP SHARED(g_lo, w0tmp, z0tmp, dtmp) &
976
    !$OMP SCHEDULE(static)
977
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
978
       ik = ik_idx(g_lo,iglo)
×
979
       it = it_idx(g_lo,iglo)
×
980
       is = is_idx(g_lo,iglo)
×
981
       do isgn=1,2
×
982
          w0tmp(:,isgn,iglo) = w0tmp(:,isgn,iglo) - z0tmp(:,isgn,iglo)*dtmp(:,it,ik,is)
×
983
       end do
984
    end do
985
    !$OMP END PARALLEL DO
986

987
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
988
! Get v1w1
989

990
    !$OMP PARALLEL DO DEFAULT(none) &
991
    !$OMP PRIVATE(iglo, ik, ie, il, is, isgn) &
992
    !$OMP SHARED(g_lo, conservative, gtmp, vns, speed, vpdiff, aj0, w0tmp, vpa) &
993
    !$OMP SCHEDULE(static)
994
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
995
       ik = ik_idx(g_lo,iglo)
×
996
       ie = ie_idx(g_lo,iglo)
×
997
       il = il_idx(g_lo,iglo)
×
998
       is = is_idx(g_lo,iglo)
×
999
       do isgn = 1, 2
×
1000
          ! v1 = nud vpa J0 f0
1001
          if (conservative) then
×
1002
             gtmp(:,isgn,iglo) = vns(ik,ie,is,1)*speed(ie)*vpdiff(:,isgn,il) &
×
1003
                  * aj0(:,iglo) * real(w0tmp(:,isgn,iglo))
×
1004
          else
1005
             gtmp(:,isgn,iglo) = vns(ik,ie,is,1)*vpa(:,isgn,iglo)*aj0(:,iglo) &
×
1006
                  * real(w0tmp(:,isgn,iglo))
×
1007
          end if
1008
       end do
1009
    end do
1010
    !$OMP END PARALLEL DO
1011

1012
    call integrate_moment (gtmp, dtmp, all_procs)    ! v1w1
×
1013

1014
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1015
! Redefine w0 = w1 - v1w1 * s1 / (1 + v1s1) (this is w2 from MAB notes)
1016

1017
    !$OMP PARALLEL DO DEFAULT(none) &
1018
    !$OMP PRIVATE(iglo, ik, it, is, isgn) &
1019
    !$OMP SHARED(g_lo, w0tmp, s0tmp, dtmp) &
1020
    !$OMP SCHEDULE(static)
1021
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
1022
       ik = ik_idx(g_lo,iglo)
×
1023
       it = it_idx(g_lo,iglo)
×
1024
       is = is_idx(g_lo,iglo)
×
1025
       do isgn=1,2
×
1026
          w0tmp(:,isgn,iglo) = w0tmp(:,isgn,iglo) - s0tmp(:,isgn,iglo)*dtmp(:,it,ik,is)
×
1027
       end do
1028
    end do
1029
    !$OMP END PARALLEL DO
1030

1031
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1032
! Get v2w2
1033

1034
    !$OMP PARALLEL DO DEFAULT(none) &
1035
    !$OMP PRIVATE(iglo, ik, ie, il, is, isgn) &
1036
    !$OMP SHARED(g_lo, gtmp, vns, energy, al, aj1, w0tmp) &
1037
    !$OMP SCHEDULE(static)
1038
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
1039
       ik = ik_idx(g_lo,iglo)
×
1040
       ie = ie_idx(g_lo,iglo)
×
1041
       il = il_idx(g_lo,iglo)
×
1042
       is = is_idx(g_lo,iglo)
×
1043
       do isgn = 1, 2
×
1044
          ! v2 = nud vperp J1 f0
1045
          ! Note : aj1 = J1(alpha) / alpha, where we have
1046
          ! alpha^2 = energy * al * smz^2 * kperp2 / bmag = vperp^2 smz^2 kperp2 / bmag^2
1047
          ! so energy * al * aj1 = (B / [kperp2 * smz^2]) alpha^2 aj1
1048
          ! = (B / [kperp2 * smz^2]) alpha J1 = vperp J1 / (kperp * smz). As w0tmp
1049
          ! appears to have an extra factor of kperp * smz / B this _may_ work out to
1050
          ! (vperp J1 / B) * ... Is there an extra factor of 1 / B here?
1051
          gtmp(:, isgn, iglo) = vns(ik, ie, is, 1) * energy(ie) * al(il) * aj1(:, iglo) &
×
1052
               * real(w0tmp(:, isgn, iglo))
×
1053
       end do
1054
    end do
1055
    !$OMP END PARALLEL DO
1056

1057
    call integrate_moment (gtmp, dtmp, all_procs)   ! v2w2
×
1058

1059
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1060
! Redefine w0 = w2 / (1 + v2w2)
1061

1062
    !$OMP PARALLEL DO DEFAULT(none) &
1063
    !$OMP PRIVATE(iglo, ik, it, is, isgn, ig) &
1064
    !$OMP SHARED(g_lo, w0tmp, dtmp) &
1065
    !$OMP SCHEDULE(static)
1066
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
1067
       ik = ik_idx(g_lo,iglo)
×
1068
       it = it_idx(g_lo,iglo)
×
1069
       is = is_idx(g_lo,iglo)
×
1070
       do isgn=1,2
×
1071
          w0tmp(:,isgn,iglo) = w0tmp(:,isgn,iglo) / (1.0 + dtmp(:,it,ik,is))
×
1072
       end do
1073
    end do
1074
    !$OMP END PARALLEL DO
1075

1076
    deallocate (gtmp, duinv, dtmp, vns)
×
1077

1078
    if(use_le_layout) then
×
1079
       allocate (z_big(-ntgrid:ntgrid, 2, g_lo%llim_proc:g_lo%ulim_alloc))
×
1080

1081
       ! first set s0le, w0le & z0le
1082
       z_big = cmplx(real(s0tmp), real(w0tmp))
×
1083

1084
       ! get rid of z0, s0, w0 now that we've converted to z0le, s0le, w0le
1085
       if (allocated(s0tmp)) deallocate(s0tmp)
×
1086
       if (allocated(w0tmp)) deallocate(w0tmp)
×
1087

1088
       call gather (g2le, z_big, ctmp)
×
1089

1090
       if (.not. allocated(s0le)) then
×
1091
          allocate (s0le(nxi_lim, negrid+1, le_lo%llim_proc:le_lo%ulim_alloc))
×
1092
          allocate (w0le(nxi_lim, negrid+1, le_lo%llim_proc:le_lo%ulim_alloc))
×
1093
       end if
1094

1095
       s0le = real(ctmp)
×
1096
       w0le = aimag(ctmp)
×
1097

1098
       ! set z0le
1099
       call gather (g2le, z0tmp, ctmp)
×
1100
       if (allocated(z0tmp)) deallocate(z0tmp)
×
1101
       if (.not. allocated(z0le)) allocate (z0le(nxi_lim, negrid+1, le_lo%llim_proc:le_lo%ulim_alloc))
×
1102
       z0le = real(ctmp)
×
1103

1104
       deallocate (ctmp, z_big)
×
1105
    else
1106
       !Only need the real components (imaginary part should be zero, just
1107
       !use complex arrays to allow reuse of existing integrate routines etc.)
1108
       if (.not. allocated(s0)) then
×
1109
          allocate (s0(-ntgrid:ntgrid,2,g_lo%llim_proc:g_lo%ulim_alloc))
×
1110
          s0=real(s0tmp)
×
1111
          deallocate(s0tmp)
×
1112
       endif
1113

1114
       if (.not. allocated(w0)) then
×
1115
          allocate (w0(-ntgrid:ntgrid,2,g_lo%llim_proc:g_lo%ulim_alloc))
×
1116
          w0=real(w0tmp)
×
1117
          deallocate(w0tmp)
×
1118
       endif
1119

1120
       if (.not. allocated(z0)) then
×
1121
          allocate (z0(-ntgrid:ntgrid,2,g_lo%llim_proc:g_lo%ulim_alloc))
×
1122
          z0=real(z0tmp)
×
1123
          deallocate(z0tmp)
×
1124
       endif
1125
    end if
1126

1127
  end subroutine init_lorentz_conserve
×
1128

1129
  !> Precompute three quantities needed for momentum and energy conservation:
1130
  !> bz0, bw0, bs0
1131
  subroutine init_diffuse_conserve
×
1132
    use gs2_layouts, only: g_lo, ie_idx, is_idx, ik_idx, il_idx, it_idx
1133
    use species, only: nspec, spec
1134
    use kt_grids, only: naky, ntheta0, kperp2
1135
    use theta_grid, only: ntgrid, bmag
1136
    use le_grids, only: energy => energy_maxw, al, integrate_moment, negrid, forbid
1137
    use gs2_time, only: code_dt
1138
    use dist_fn_arrays, only: aj0, aj1, vpa
1139
    use le_grids, only: g2le
1140
    use gs2_layouts, only: le_lo
1141
    use redistribute, only: gather, scatter
1142
    use array_utils, only: zero_array
1143
    implicit none
1144
    real, dimension (:,:,:), allocatable :: gtmp
×
1145
    real, dimension (:,:,:,:), allocatable :: duinv, dtmp, vns
×
1146
    integer :: ie, il, ik, is, isgn, iglo, it, ig
1147
    complex, dimension (:,:,:), allocatable :: ctmp, z_big
×
1148
    complex, dimension (:,:,:), allocatable :: bs0tmp, bw0tmp, bz0tmp
×
1149
    logical, parameter :: all_procs = .true.
1150

1151
    if(use_le_layout) then
×
1152
       allocate (ctmp(nxi_lim, negrid+1, le_lo%llim_proc:le_lo%ulim_alloc))
×
1153
       ! We need to initialise ctmp as it is used as receiving buffer in
1154
       ! g2le redistribute, which doesn't populate all elements
1155
       call zero_array(ctmp)
×
1156
    end  if
1157

1158
    allocate(bs0tmp(-ntgrid:ntgrid,2,g_lo%llim_proc:g_lo%ulim_alloc))
×
1159
    allocate(bw0tmp(-ntgrid:ntgrid,2,g_lo%llim_proc:g_lo%ulim_alloc))
×
1160
    allocate(bz0tmp(-ntgrid:ntgrid,2,g_lo%llim_proc:g_lo%ulim_alloc))
×
1161

1162
    allocate (gtmp(-ntgrid:ntgrid,2,g_lo%llim_proc:g_lo%ulim_alloc))
×
1163
    allocate (duinv(-ntgrid:ntgrid, ntheta0, naky, nspec))
×
1164
    allocate (dtmp(-ntgrid:ntgrid, ntheta0, naky, nspec))
×
1165
    allocate (vns(naky,negrid,nspec,2))
×
1166

1167
    ! Following might only be needed if any kwork_filter
1168
    call zero_array(duinv)
×
1169
    call zero_array(dtmp)
×
1170

1171
    vns(:,:,:,1) = vnmult(2)*delvnew
×
1172
    vns(:,:,:,2) = vnmult(2)*vnew_s
×
1173

1174
    ! first obtain 1/du
1175
    !$OMP PARALLEL DO DEFAULT(none) &
1176
    !$OMP PRIVATE(iglo, ik, ie, is, isgn) &
1177
    !$OMP SHARED(g_lo, gtmp, energy, vnmult, vnew_E) &
1178
    !$OMP SCHEDULE(static)
1179
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
1180
       ik = ik_idx(g_lo,iglo)
×
1181
       ie = ie_idx(g_lo,iglo)
×
1182
       is = is_idx(g_lo,iglo)
×
1183
       do isgn = 1, 2
×
1184
          gtmp(:,isgn,iglo) = energy(ie)*vnmult(2)*vnew_E(ik,ie,is)
×
1185
       end do
1186
    end do
1187
    !$OMP END PARALLEL DO
1188

1189
    call integrate_moment (gtmp, duinv, all_procs)  ! not 1/du yet
×
1190

1191
    ! Could replace this with OpenMP using an explicit loop. TAG
1192
    where (abs(duinv) > epsilon(0.0))  ! necessary b/c some species may have vnewk=0
×
1193
       !  duinv=0 iff vnew=0 so ok to keep duinv=0.
1194
       duinv = 1 / duinv  ! now it is 1/du
×
1195
    end where
1196

1197
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1198
    ! Now get z0 (first form)
1199
    !$OMP PARALLEL DO DEFAULT(none) &
1200
    !$OMP PRIVATE(iglo, ik, it, ie, il, is, isgn, ig) &
1201
    !$OMP SHARED(g_lo, ntgrid, forbid, bz0tmp, code_dt, vnmult, vnew_E, &
1202
    !$OMP aj0, duinv, conserve_forbid_zero) &
1203
    !$OMP SCHEDULE(static)
1204
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
1205
       it = it_idx(g_lo,iglo)
×
1206
       ik = ik_idx(g_lo,iglo)
×
1207
       ie = ie_idx(g_lo,iglo)
×
1208
       is = is_idx(g_lo,iglo)
×
1209
       il = il_idx(g_lo,iglo)
×
1210
       do isgn = 1, 2
×
1211
          do ig=-ntgrid,ntgrid
×
1212
             ! u0 = -nu_E E dt J0 f_0 / du
1213
             if ( .not. (forbid(ig,il) .and. conserve_forbid_zero) ) then
×
1214
                bz0tmp(ig,isgn,iglo) = -code_dt*vnmult(2)*vnew_E(ik,ie,is) &
×
1215
                     * aj0(ig,iglo)*duinv(ig,it,ik,is)
×
1216
             else
1217
                bz0tmp(ig,isgn,iglo) = 0.
×
1218
             endif
1219
          end do
1220
       end do
1221
    end do
1222
    !$OMP END PARALLEL DO
1223

1224
    call zero_out_passing_hybrid_electrons(bz0tmp)
×
1225

1226
    if(use_le_layout) then
×
1227
       call gather (g2le, bz0tmp, ctmp)
×
1228
       call solfp_ediffuse_le_layout (ctmp)
×
1229
       call scatter (g2le, ctmp, bz0tmp)   ! bz0 is redefined below
×
1230
    else
1231
       call solfp_ediffuse_standard_layout (bz0tmp)   ! bz0 is redefined below
×
1232
    end if
1233

1234
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1235
! Now get v0z0
1236

1237
    !$OMP PARALLEL DO DEFAULT(none) &
1238
    !$OMP PRIVATE(iglo, ik, ie, is, isgn) &
1239
    !$OMP SHARED(g_lo, gtmp, vnmult, vnew_E, aj0, bz0tmp) &
1240
    !$OMP SCHEDULE(static)
1241
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
1242
       ik = ik_idx(g_lo,iglo)
×
1243
       ie = ie_idx(g_lo,iglo)
×
1244
       is = is_idx(g_lo,iglo)
×
1245
       do isgn = 1, 2
×
1246
          ! v0 = nu_E E J0 f_0
1247
          gtmp(:,isgn,iglo) = vnmult(2) * vnew_E(ik,ie,is) * aj0(:,iglo) &
×
1248
               * real(bz0tmp(:,isgn,iglo))
×
1249
       end do
1250
    end do
1251
    !$OMP END PARALLEL DO
1252

1253
    call integrate_moment (gtmp, dtmp, all_procs) ! v0z0
×
1254

1255
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1256
! Redefine z0 = z0 / (1 + v0z0)
1257

1258
    !$OMP PARALLEL DO DEFAULT(none) &
1259
    !$OMP PRIVATE(iglo, ik, it, is, isgn) &
1260
    !$OMP SHARED(g_lo, bz0tmp, dtmp) &
1261
    !$OMP SCHEDULE(static)
1262
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
1263
       it = it_idx(g_lo,iglo)
×
1264
       ik = ik_idx(g_lo,iglo)
×
1265
       is = is_idx(g_lo,iglo)
×
1266
       do isgn = 1, 2
×
1267
          bz0tmp(:,isgn,iglo) = bz0tmp(:,isgn,iglo) / (1.0 + dtmp(:,it,ik,is))
×
1268
       end do
1269
    end do
1270
    !$OMP END PARALLEL DO
1271

1272
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1273

1274
    ! redefine dq = du (for momentum-conserving terms)
1275
    ! du == int (E nu_s f_0);  du = du(z, kx, ky, s)
1276
    ! duinv = 1/du
1277
    !$OMP PARALLEL DO DEFAULT(none) &
1278
    !$OMP PRIVATE(iglo, ik, ie, is, isgn) &
1279
    !$OMP SHARED(g_lo, gtmp, vns, vpa) &
1280
    !$OMP SCHEDULE(static)
1281
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
1282
       ik = ik_idx(g_lo,iglo)
×
1283
       ie = ie_idx(g_lo,iglo)
×
1284
       is = is_idx(g_lo,iglo)
×
1285
       do isgn = 1, 2
×
1286
          gtmp(:, isgn, iglo)  = vns(ik, ie, is, 1) * vpa(:, isgn, iglo)**2
×
1287
       end do
1288
    end do
1289
    !$OMP END PARALLEL DO
1290

1291
    call integrate_moment (gtmp, duinv, all_procs)  ! not 1/du yet
×
1292

1293
    ! Could replace this with OpenMP using an explicit loop. TAG
1294
    where (abs(duinv) > epsilon(0.0))  ! necessary b/c some species may have vnewk=0
×
1295
       !  duinv=0 iff vnew=0 so ok to keep duinv=0.
1296
       duinv = 1 / duinv  ! now it is 1/du
×
1297
    end where
1298

1299
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1300
! Now get s0 (first form)
1301
    !$OMP PARALLEL DO DEFAULT(none) &
1302
    !$OMP PRIVATE(iglo, ik, it, ie, is, isgn) &
1303
    !$OMP SHARED(g_lo, bs0tmp, vns, vpa, aj0, code_dt, duinv) &
1304
    !$OMP SCHEDULE(static)
1305
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
1306
       it = it_idx(g_lo,iglo)
×
1307
       ik = ik_idx(g_lo,iglo)
×
1308
       ie = ie_idx(g_lo,iglo)
×
1309
       is = is_idx(g_lo,iglo)
×
1310
       do isgn = 1, 2
×
1311
          ! u1 = -3 nu_s vpa dt J0 f_0 / du
1312
          bs0tmp(:, isgn, iglo) = -vns(ik, ie, is, 1) * vpa(:, isgn, iglo) &
×
1313
               * aj0(:, iglo) * code_dt * duinv(:, it, ik, is)
×
1314
       end do
1315
    end do
1316
    !$OMP END PARALLEL DO
1317

1318
    call zero_out_passing_hybrid_electrons(bs0tmp)
×
1319

1320
    if(use_le_layout) then
×
1321
       call gather (g2le, bs0tmp, ctmp)
×
1322
       call solfp_ediffuse_le_layout (ctmp)
×
1323
       call scatter (g2le, ctmp, bs0tmp)   ! bs0
×
1324
    else
1325
       call solfp_ediffuse_standard_layout (bs0tmp)    ! s0
×
1326
    end if
1327

1328
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1329
! Now get v0s0
1330

1331
    !$OMP PARALLEL DO DEFAULT(none) &
1332
    !$OMP PRIVATE(iglo, ik, ie, is, isgn) &
1333
    !$OMP SHARED(g_lo, gtmp, vnmult, vnew_E, aj0, bs0tmp) &
1334
    !$OMP SCHEDULE(static)
1335
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
1336
       ik = ik_idx(g_lo,iglo)
×
1337
       ie = ie_idx(g_lo,iglo)
×
1338
       is = is_idx(g_lo,iglo)
×
1339
       do isgn = 1, 2
×
1340
          ! v0 = nu_E E J0
1341
          gtmp(:, isgn, iglo) = vnmult(2) * vnew_E(ik, ie, is) * aj0(:, iglo) &
×
1342
               * real(bs0tmp(:, isgn, iglo))
×
1343
       end do
1344
    end do
1345
    !$OMP END PARALLEL DO
1346

1347
    call integrate_moment (gtmp, dtmp, all_procs)    ! v0s0
×
1348

1349
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1350
! Redefine s0 = s0 - v0s0 * z0 / (1 + v0z0)
1351

1352
    !$OMP PARALLEL DO DEFAULT(none) &
1353
    !$OMP PRIVATE(iglo, ik, it, is, isgn) &
1354
    !$OMP SHARED(g_lo, bs0tmp, dtmp, bz0tmp) &
1355
    !$OMP SCHEDULE(static)
1356
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
1357
       ik = ik_idx(g_lo,iglo)
×
1358
       it = it_idx(g_lo,iglo)
×
1359
       is = is_idx(g_lo,iglo)
×
1360
       do isgn=1,2
×
1361
          bs0tmp(:, isgn, iglo) = bs0tmp(:, isgn, iglo) - dtmp(:, it, ik, is) &
×
1362
               * bz0tmp(:, isgn, iglo)
×
1363
       end do
1364
    end do
1365
    !$OMP END PARALLEL DO
1366

1367
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1368
! Now get v1s0
1369

1370
    !$OMP PARALLEL DO DEFAULT(none) &
1371
    !$OMP PRIVATE(iglo, ik, ie, is, isgn) &
1372
    !$OMP SHARED(g_lo, gtmp, vns, vpa, aj0, bs0tmp) &
1373
    !$OMP SCHEDULE(static)
1374
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
1375
       ik = ik_idx(g_lo,iglo)
×
1376
       ie = ie_idx(g_lo,iglo)
×
1377
       is = is_idx(g_lo,iglo)
×
1378
       do isgn = 1, 2
×
1379
          ! v1 = (nu_s - nu_D) vpa J0
1380
          gtmp(:, isgn, iglo) = vns(ik, ie, is, 1) * vpa(:, isgn, iglo) * aj0(:, iglo) &
×
1381
               * real(bs0tmp(:, isgn, iglo))
×
1382
       end do
1383
    end do
1384
    !$OMP END PARALLEL DO
1385

1386
    call integrate_moment (gtmp, dtmp, all_procs)    ! v1s0
×
1387

1388
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1389
! Redefine s0 = s0 / (1 + v0s0)
1390

1391
    !$OMP PARALLEL DO DEFAULT(none) &
1392
    !$OMP PRIVATE(iglo, ik, it, is, isgn) &
1393
    !$OMP SHARED(g_lo, bs0tmp, dtmp) &
1394
    !$OMP SCHEDULE(static)
1395
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
1396
       ik = ik_idx(g_lo,iglo)
×
1397
       it = it_idx(g_lo,iglo)
×
1398
       is = is_idx(g_lo,iglo)
×
1399
       do isgn=1,2
×
1400
          bs0tmp(:, isgn, iglo) = bs0tmp(:, isgn, iglo) / (1.0 + dtmp(:, it, ik, is))
×
1401
       end do
1402
    end do
1403
    !$OMP END PARALLEL DO
1404

1405
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1406
! Now get w0
1407
    !$OMP PARALLEL DO DEFAULT(none) &
1408
    !$OMP PRIVATE(iglo, ik, it, ie, il, is, isgn, ig) &
1409
    !$OMP SHARED(g_lo, ntgrid, forbid, bw0tmp, vns, energy, al, aj1, code_dt, &
1410
    !$OMP spec, kperp2, duinv, bmag, conserve_forbid_zero) &
1411
    !$OMP SCHEDULE(static)
1412
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
1413
       it = it_idx(g_lo,iglo)
×
1414
       ik = ik_idx(g_lo,iglo)
×
1415
       ie = ie_idx(g_lo,iglo)
×
1416
       il = il_idx(g_lo,iglo)
×
1417
       is = is_idx(g_lo,iglo)
×
1418
       do isgn = 1, 2
×
1419
          do ig=-ntgrid,ntgrid
×
1420
             ! u0 = -3 dt J1 vperp vus a f0 / du
1421
             if ( .not. (forbid(ig,il) .and. conserve_forbid_zero)) then
×
1422
                ! Note: energy * al * smz^2 * kperp2 / bmag is alpha^2 where
1423
                ! alpha is the argument to the Bessel function, i.e. aj1 = J1(alpha) / alpha
1424
                ! This appears to leave us with alpha * J1(alpha) whilst Barnes' paper
1425
                ! only includes terms with J1(alpha). Note that alpha = vperp kperp smz/B
1426
                ! so alpha * J1(alpha) = (kperp * smz / B) * vperp J1
1427
                bw0tmp(ig, isgn, iglo) = -vns(ik, ie, is, 1) * energy(ie) * al(il) &
×
1428
                     * aj1(ig, iglo) * code_dt * spec(is)%smz**2 * kperp2(ig, it, ik) * &
×
1429
                     duinv(ig, it, ik, is) / bmag(ig)
×
1430
             else
1431
                bw0tmp(ig, isgn, iglo) = 0.
×
1432
             endif
1433
          end do
1434
       end do
1435
    end do
1436
    !$OMP END PARALLEL DO
1437

1438
    call zero_out_passing_hybrid_electrons(bw0tmp)
×
1439

1440
    if(use_le_layout) then
×
1441
       call gather (g2le, bw0tmp, ctmp)
×
1442
       call solfp_ediffuse_le_layout (ctmp)
×
1443
       call scatter (g2le, ctmp, bw0tmp)
×
1444
    else
1445
       call solfp_ediffuse_standard_layout (bw0tmp)
×
1446
    end if
1447

1448
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1449
! Now get v0w0
1450

1451
    !$OMP PARALLEL DO DEFAULT(none) &
1452
    !$OMP PRIVATE(iglo, ik, ie, is, isgn) &
1453
    !$OMP SHARED(g_lo, gtmp, vnmult, vnew_E, aj0, bw0tmp) &
1454
    !$OMP SCHEDULE(static)
1455
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
1456
       ik = ik_idx(g_lo,iglo)
×
1457
       ie = ie_idx(g_lo,iglo)
×
1458
       is = is_idx(g_lo,iglo)
×
1459
       do isgn = 1, 2
×
1460
          ! v0 = nu_E E J0
1461
          gtmp(:, isgn, iglo) = vnmult(2) * vnew_E(ik, ie, is) * aj0(:, iglo) &
×
1462
               * real(bw0tmp(:, isgn, iglo))
×
1463
       end do
1464
    end do
1465
    !$OMP END PARALLEL DO
1466

1467
    call integrate_moment (gtmp, dtmp, all_procs)    ! v0w0
×
1468

1469
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1470
! Redefine w0 = w0 - v0w0 * z0 / (1 + v0z0) (this is w1 from MAB notes)
1471

1472
    !$OMP PARALLEL DO DEFAULT(none) &
1473
    !$OMP PRIVATE(iglo, ik, it, is, isgn) &
1474
    !$OMP SHARED(g_lo, bw0tmp, bz0tmp, dtmp) &
1475
    !$OMP SCHEDULE(static)
1476
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
1477
       ik = ik_idx(g_lo,iglo)
×
1478
       it = it_idx(g_lo,iglo)
×
1479
       is = is_idx(g_lo,iglo)
×
1480
       do isgn = 1, 2
×
1481
          bw0tmp(:, isgn, iglo) = bw0tmp(:, isgn, iglo) - bz0tmp(:, isgn, iglo) &
×
1482
               * dtmp(:, it, ik, is)
×
1483
       end do
1484
    end do
1485
    !$OMP END PARALLEL DO
1486

1487
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1488
! Get v1w1
1489

1490
    !$OMP PARALLEL DO DEFAULT(none) &
1491
    !$OMP PRIVATE(iglo, ik, ie, is, isgn) &
1492
    !$OMP SHARED(g_lo, gtmp, vns, vpa, aj0, bw0tmp) &
1493
    !$OMP SCHEDULE(static)
1494
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
1495
       ik = ik_idx(g_lo,iglo)
×
1496
       ie = ie_idx(g_lo,iglo)
×
1497
       is = is_idx(g_lo,iglo)
×
1498
       do isgn = 1, 2
×
1499
          ! v1 = (nus-nud) vpa J0 f0
1500
          gtmp(:, isgn, iglo) = vns(ik, ie, is, 1) * vpa(:, isgn, iglo) * aj0(:, iglo) &
×
1501
               * real(bw0tmp(:, isgn, iglo))
×
1502
       end do
1503
    end do
1504
    !$OMP END PARALLEL DO
1505

1506
    call integrate_moment (gtmp, dtmp, all_procs)    ! v1w1
×
1507

1508
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1509
! Redefine w0 = w1 - v1w1 * s1 / (1 + v1s1) (this is w2 from MAB notes)
1510

1511
    !$OMP PARALLEL DO DEFAULT(none) &
1512
    !$OMP PRIVATE(iglo, ik, it, is, isgn) &
1513
    !$OMP SHARED(g_lo, bw0tmp, bs0tmp, dtmp) &
1514
    !$OMP SCHEDULE(static)
1515
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
1516
       ik = ik_idx(g_lo,iglo)
×
1517
       it = it_idx(g_lo,iglo)
×
1518
       is = is_idx(g_lo,iglo)
×
1519
       do isgn = 1, 2
×
1520
          bw0tmp(:, isgn, iglo) = bw0tmp(:, isgn, iglo) - bs0tmp(:, isgn, iglo) &
×
1521
               * dtmp(:, it, ik, is)
×
1522
       end do
1523
    end do
1524
    !$OMP END PARALLEL DO
1525

1526
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1527
! Get v2w2
1528

1529
    !$OMP PARALLEL DO DEFAULT(none) &
1530
    !$OMP PRIVATE(iglo, ik, ie, il, is, isgn) &
1531
    !$OMP SHARED(g_lo, gtmp, vns, energy, al, aj1, bw0tmp) &
1532
    !$OMP SCHEDULE(static)
1533
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
1534
       ik = ik_idx(g_lo,iglo)
×
1535
       ie = ie_idx(g_lo,iglo)
×
1536
       il = il_idx(g_lo,iglo)
×
1537
       is = is_idx(g_lo,iglo)
×
1538
       do isgn = 1, 2
×
1539
          ! v2 = (nus-nud) vperp J1 f0
1540
          ! Note : aj1 = J1(alpha) / alpha, where we have
1541
          ! alpha^2 = energy * al * smz^2 * kperp2 / bmag = vperp^2 smz^2 kperp2 / bmag^2
1542
          ! so energy * al * aj1 = (B / [kperp2 * smz^2]) alpha^2 aj1
1543
          ! = (B / [kperp2 * smz^2]) alpha J1 = vperp J1 / (kperp * smz). As w0tmp
1544
          ! appears to have an extra factor of kperp * smz / B this _may_ work out to
1545
          ! (vperp J1 / B) * ... Is there an extra factor of 1 / B here?
1546
          gtmp(:, isgn, iglo) = vns(ik, ie, is, 1) * energy(ie) * al(il) * aj1(:, iglo) &
×
1547
               * real(bw0tmp(:, isgn, iglo))
×
1548
       end do
1549
    end do
1550
    !$OMP END PARALLEL DO
1551

1552
    call integrate_moment (gtmp, dtmp, all_procs)   ! v2w2
×
1553

1554
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1555
! Redefine w0 = w2 / (1 + v2w2)
1556

1557
    !$OMP PARALLEL DO DEFAULT(none) &
1558
    !$OMP PRIVATE(iglo, ik, it, is, isgn) &
1559
    !$OMP SHARED(g_lo, bw0tmp, dtmp) &
1560
    !$OMP SCHEDULE(static)
1561
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
1562
       ik = ik_idx(g_lo,iglo)
×
1563
       it = it_idx(g_lo,iglo)
×
1564
       is = is_idx(g_lo,iglo)
×
1565
       do isgn=1,2
×
1566
          bw0tmp(:, isgn, iglo) = bw0tmp(:, isgn, iglo) / (1.0 + dtmp(:, it, ik, is))
×
1567
       end do
1568
    end do
1569
    !$OMP END PARALLEL DO
1570

1571
    deallocate (gtmp, duinv, dtmp, vns)
×
1572

1573
    if(use_le_layout) then
×
1574
       allocate (z_big(-ntgrid:ntgrid, 2, g_lo%llim_proc:g_lo%ulim_alloc))
×
1575
       z_big=cmplx(real(bs0tmp),real(bw0tmp))
×
1576
       deallocate (bs0tmp)
×
1577
       deallocate (bw0tmp)
×
1578
       call gather (g2le, z_big, ctmp)
×
1579
       deallocate(z_big)
×
1580
       if (.not. allocated(bs0le)) allocate (bs0le(nxi_lim, negrid+1, le_lo%llim_proc:le_lo%ulim_alloc))
×
1581
       if (.not. allocated(bw0le)) allocate (bw0le(nxi_lim, negrid+1, le_lo%llim_proc:le_lo%ulim_alloc))
×
1582
       bs0le = real(ctmp)
×
1583
       bw0le = aimag(ctmp)
×
1584

1585
       call gather (g2le, bz0tmp, ctmp)
×
1586
       deallocate (bz0tmp)
×
1587
       if (.not. allocated(bz0le)) allocate (bz0le(nxi_lim, negrid+1, le_lo%llim_proc:le_lo%ulim_alloc))
×
1588
       bz0le = real(ctmp)
×
1589
       deallocate (ctmp)
×
1590
    else
1591
       !Only need the real components (imaginary part should be zero, just
1592
       !use complex arrays to allow reuse of existing integrate routines etc.)
1593
       if (.not. allocated(bs0)) then
×
1594
          allocate (bs0(-ntgrid:ntgrid,2,g_lo%llim_proc:g_lo%ulim_alloc))
×
1595
          bs0=real(bs0tmp)
×
1596
          deallocate(bs0tmp)
×
1597
       endif
1598

1599
       if (.not. allocated(bw0)) then
×
1600
          allocate (bw0(-ntgrid:ntgrid,2,g_lo%llim_proc:g_lo%ulim_alloc))
×
1601
          bw0=real(bw0tmp)
×
1602
          deallocate(bw0tmp)
×
1603
       endif
1604

1605
       if (.not. allocated(bz0)) then
×
1606
          allocate (bz0(-ntgrid:ntgrid,2,g_lo%llim_proc:g_lo%ulim_alloc))
×
1607
          bz0=real(bz0tmp)
×
1608
          deallocate(bz0tmp)
×
1609
       endif
1610
    end if
1611

1612
  end subroutine init_diffuse_conserve
×
1613
  
1614
  !> If we have hybrid electrons then we need to remove the
1615
  !> contribution to the conservation terms from the adiabatic
1616
  !> passing part. This routine does this for us.
1617
  subroutine zero_out_passing_hybrid_electrons(arr)
×
1618
    use gs2_layouts, only: g_lo, ik_idx, il_idx, is_idx
1619
    use species, only: has_hybrid_electron_species, spec
1620
    use le_grids, only: is_passing_hybrid_electron
1621
    implicit none
1622
    complex, dimension(:, :, g_lo%llim_proc:), intent(in out) :: arr
1623
    integer :: iglo, is, ik, il
1624
    if (has_hybrid_electron_species(spec)) then
×
1625
       !$OMP PARALLEL DO DEFAULT(none) &
1626
       !$OMP PRIVATE(iglo, ik, il, is) &
1627
       !$OMP SHARED(g_lo, arr) &
1628
       !$OMP SCHEDULE(static)
1629
       do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
1630
          ik = ik_idx(g_lo,iglo)
×
1631
          il = il_idx(g_lo,iglo)
×
1632
          is = is_idx(g_lo,iglo)
×
1633
          if (is_passing_hybrid_electron(is, ik, il)) arr(:, :, iglo) = 0.0
×
1634
       end do
1635
       !$OMP END PARALLEL DO
1636
    end if
1637
  end subroutine zero_out_passing_hybrid_electrons
×
1638

1639
  !> FIXME : Add documentation
1640
  subroutine init_vnew
×
1641
    use species, only: nspec, spec, is_electron_species
1642
    use le_grids, only: negrid, energy => energy_maxw, w => w_maxw
1643
    use kt_grids, only: naky, ntheta0, kperp2
1644
    use theta_grid, only: ntgrid
1645
    use run_parameters, only: zeff, delt_option_switch, delt_option_auto
1646
    use gs2_time, only: tunits
1647
    use constants, only: pi, sqrt_pi
1648
    use gs2_save, only: init_vnm
1649
    use warning_helpers, only: exactly_equal
1650
    real, dimension(negrid):: hee, hsg, local_energy
×
1651
    integer :: ik, ie, is, it
1652
    real :: k4max
1653
    real :: vl, vr, dv2l, dv2r
1654
    real, dimension (2) :: vnm_saved
1655

1656
    if (delt_option_switch == delt_option_auto) then
×
1657
       call init_vnm (vnm_saved)
×
1658
       vnm_init = vnm_saved
×
1659
    endif
1660

1661
    ! Initialise vnmult from the values in run_parameters. This is
1662
    ! either what has been restored from the restart file  if
1663
    ! `delt_option == 'check_restart'` or 1 otherwise.
1664
    if (all(exactly_equal(vnmult, -1.))) then
×
1665
       vnmult = vnm_init
×
1666
    end if
1667

1668
    if (const_v) then
×
1669
       local_energy = 1.0
×
1670
    else
1671
       local_energy = energy
×
1672
    end if
1673

1674
    do ie = 1, negrid
×
1675
       hee(ie) = exp(-local_energy(ie))/sqrt(pi*local_energy(ie)) &
×
1676
            + (1.0 - 0.5/local_energy(ie))*erf(sqrt(local_energy(ie)))
×
1677
       !>MAB
1678
       ! hsg is the G of Hirshman and Sigmar
1679
       ! added to allow for momentum conservation with energy diffusion
1680
       hsg(ie) = hsg_func(sqrt(local_energy(ie)))
×
1681
       !<MAB
1682
    end do
1683

1684
    if (.not.allocated(vnew)) then
×
1685
       ! Here the ky dependence is just to allow us to scale by tunits.
1686
       ! This saves some operations in later use at expense of more memory
1687
       allocate (vnew(naky,negrid,nspec))
×
1688
       allocate (vnew_s(naky,negrid,nspec))
×
1689
       allocate (vnew_D(naky,negrid,nspec))
×
1690
       allocate (vnew_E(naky,negrid,nspec))
×
1691
       allocate (delvnew(naky,negrid,nspec))
×
1692
    end if
1693

1694
    if (ei_coll_only) then
×
1695
       vnew_s = 0 ; vnew_D = 0 ; vnew_E = 0 ; delvnew = 0.
×
1696
       do is = 1, nspec
×
1697
          if (is_electron_species(spec(is))) then
×
1698
             do ie = 1, negrid
×
1699
                vnew(:, ie, is) = spec(is)%vnewk / local_energy(ie)**1.5 &
×
1700
                        * zeff * 0.5 * tunits(:)
×
1701
             end do
1702
          else
1703
             vnew(:, :, is) = 0.0
×
1704
          end if
1705
       end do
1706
    else
1707
       do is = 1, nspec
×
1708
          ! Don't include following lines, as haven't yet accounted for possibility of timestep changing.
1709
          ! Correct collision frequency if applying collisions only every nth timestep:
1710
          !spec(is)%vnewk = spec(is)%vnewk * float(timesteps_between_collisions)
1711

1712
          do ie = 1, negrid
×
1713
             vnew_s(:, ie, is) = spec(is)%vnewk / sqrt(local_energy(ie)) &
×
1714
                  * hsg(ie) * 4.0 * tunits(:)
×
1715
          end do
1716

1717
          if (is_electron_species(spec(is))) then
×
1718
             do ie = 1, negrid
×
1719
                vnew(:, ie, is) = spec(is)%vnewk / local_energy(ie)**1.5 &
×
1720
                     * (zeff + hee(ie)) * 0.5 * tunits(:)
×
1721
                vnew_D(:, ie, is) = spec(is)%vnewk / local_energy(ie)**1.5 &
×
1722
                     * hee(ie) * tunits(:)
×
1723
             end do
1724
          else
1725
             do ie = 1, negrid
×
1726
                vnew(:, ie, is) = spec(is)%vnewk / local_energy(ie)**1.5 &
×
1727
                     * hee(ie) * 0.5 * tunits(:)
×
1728
                vnew_D(:, ie, is) = 2.0 * vnew(:, ie, is)
×
1729
             end do
1730
          end if
1731
       end do
1732
    end if
1733

1734
    if (conservative) then !Presumably not compatible with const_v so we use energy
×
1735
       do is = 1, nspec
×
1736
          do ie = 2, negrid-1
×
1737
             vr = 0.5*(sqrt(energy(ie+1)) + sqrt(energy(ie)))
×
1738
             vl = 0.5*(sqrt(energy(ie  )) + sqrt(energy(ie-1)))
×
1739
             dv2r = (energy(ie+1) - energy(ie)) / (sqrt(energy(ie+1)) - sqrt(energy(ie)))
×
1740
             dv2l = (energy(ie) - energy(ie-1)) / (sqrt(energy(ie)) - sqrt(energy(ie-1)))
×
1741

1742
             vnew_E(:, ie, is) =  spec(is)%vnewk * tunits * &
×
1743
                  vnew_E_conservative(vr, vl, dv2r, dv2l) / (sqrt_pi * w(ie))
×
1744
             delvnew(:, ie, is) = spec(is)%vnewk * tunits * &
×
1745
                  delvnew_conservative(vr, vl) / (sqrt_pi * sqrt(energy(ie)) * w(ie))
×
1746
          end do
1747

1748
          ! boundary at v = 0
1749
          ie = 1
×
1750
          vr = 0.5 * (sqrt(energy(ie+1)) + sqrt(energy(ie)))
×
1751
          vl = 0.0
×
1752
          dv2r = (energy(ie+1) - energy(ie)) / (sqrt(energy(ie+1)) - sqrt(energy(ie)))
×
1753
          dv2l = 0.0
×
1754
          vnew_E(:, ie, is) =  spec(is)%vnewk * tunits * &
×
1755
               vnew_E_conservative(vr, vl, dv2r, dv2l) / (sqrt_pi * w(ie))
×
1756
          delvnew(:, ie, is) = spec(is)%vnewk * tunits * &
×
1757
               delvnew_conservative(vr, vl) / (sqrt_pi * sqrt(energy(ie)) * w(ie))
×
1758

1759
          ! boundary at v -> infinity
1760
          ie = negrid
×
1761
          vr = 0.0
×
1762
          vl = 0.5 * (sqrt(energy(ie)) + sqrt(energy(ie-1)))
×
1763
          dv2r = 0.0
×
1764
          dv2l = (energy(ie) - energy(ie-1)) / (sqrt(energy(ie)) - sqrt(energy(ie-1)))
×
1765
          vnew_E(:, ie, is) =  spec(is)%vnewk * tunits * &
×
1766
               vnew_E_conservative(vr, vl, dv2r, dv2l) / (sqrt_pi * w(ie))
×
1767
          delvnew(:, ie, is) = spec(is)%vnewk * tunits * &
×
1768
               delvnew_conservative(vr, vl) / (sqrt_pi * sqrt(energy(ie)) * w(ie))
×
1769
       end do
1770
    else
1771
       do is = 1, nspec
×
1772
          do ie = 2, negrid-1
×
1773
             vnew_E(:, ie, is) = local_energy(ie) * (vnew_s(:, ie, is) * &
×
1774
                  (2.0 - 0.5 / local_energy(ie)) - 2.0 * vnew_D(:, ie, is))
×
1775
             delvnew(:, ie, is) = vnew_s(:, ie, is) - vnew_D(:, ie, is)
×
1776
          end do
1777
       end do
1778
    end if
1779

1780
       ! add hyper-terms inside collision operator
1781
!BD: Warning!
1782
!BD: For finite magnetic shear, this is different in form from what appears in hyper.f90
1783
!BD: because kperp2 /= akx**2 + aky**2;  there are cross terms that are dropped in hyper.f90
1784
!BD: Warning!
1785
!BD: Also: there is no "grid_norm" option here and the exponent is fixed to 4 for now
1786
    if (hyper_colls) then
×
1787
       if (.not. allocated(vnewh)) allocate (vnewh(-ntgrid:ntgrid,ntheta0,naky,nspec))
×
1788
       k4max = (maxval(kperp2))**2
×
1789
       do is = 1, nspec
×
1790
          do ik = 1, naky
×
1791
             do it = 1, ntheta0
×
1792
                vnewh(:,it,ik,is) = spec(is)%nu_h * kperp2(:,it,ik)**2/k4max
×
1793
             end do
1794
          end do
1795
       end do
1796
    end if
1797

1798
  contains
1799
    elemental real function vnew_E_conservative(vr, vl, dv2r, dv2l) result(vnE)
×
1800
      real, intent(in) :: vr, vl, dv2l, dv2r
1801
      vnE = (vl * exp(-vl**2) * dv2l * hsg_func(vl) - vr * exp(-vr**2) * dv2r * hsg_func(vr))
×
1802
    end function vnew_E_conservative
×
1803

1804
    elemental real function delvnew_conservative(vr, vl) result(delVn)
×
1805
      real, intent(in) :: vr, vl
1806
      delVn = (vl * exp(-vl**2) * hsg_func(vl) - vr * exp(-vr**2) * hsg_func(vr))
×
1807
    end function delvnew_conservative
×
1808

1809
  end subroutine init_vnew
1810

1811
  !> FIXME : Add documentation  
1812
  elemental real function hsg_func (vel)
×
1813
    use constants, only: sqrt_pi
1814
    implicit none
1815
    real, intent (in) :: vel
1816
    if (abs(vel) <= epsilon(0.0)) then
×
1817
       hsg_func = 0.0
×
1818
    else
1819
       hsg_func = 0.5 * erf(vel) / vel**2 - exp(-vel**2) / (sqrt_pi * vel)
×
1820
    end if
1821
  end function hsg_func
×
1822

1823
  !> Given estimates of the velocity space integration errors adjust the collision
1824
  !> frequency scaling factor vnmult.
1825
  subroutine adjust_vnmult(errest, consider_trapped_error)
×
1826
    implicit none
1827
    real, dimension(5, 2), intent(in) :: errest
1828
    logical, intent(in) :: consider_trapped_error
1829
    real :: vnmult_target
1830
    logical, parameter :: increase = .true., decrease = .false.
1831

1832
    if (vary_vnew) then
×
1833
       ! Energy resolution requirements
1834
       vnmult_target = vnmult(2)
×
1835

1836
       if (errest(1,2) > etol + ewindow .or. errest(4,2) > etola + ewindowa) then
×
1837
          vnmult_target = get_vnewk (vnmult(2), increase)
×
1838
       else if (errest(1,2) < etol - ewindow .and. errest(4,2) < etola - ewindowa) then
×
1839
          vnmult_target = get_vnewk (vnmult(2), decrease)
×
1840
       end if
1841

1842
       call init_ediffuse (vnmult_target)
×
1843
       call init_diffuse_conserve
×
1844

1845
       ! Lambda resolution requirements
1846
       vnmult_target = vnmult(1)
×
1847

1848
       if (errest(2,2) > etol + ewindow .or. errest(3,2) > etol + ewindow &
1849
            .or. errest(5,2) > etola + ewindowa) then
×
1850
          vnmult_target = get_vnewk (vnmult(1), increase)
×
1851
       else if (errest(2,2) < etol - ewindow .and. errest(5,2) < etola - ewindowa .and. &
×
1852
            (errest(3,2) < etol - ewindow .or. .not. consider_trapped_error)) then
1853
          !The last conditional in the above says to ignore the trapped_error if
1854
          !we haven't calculated it. The compute_trapped_error part is probably not needed
1855
          !as errest(3,2) should be zero there anyway.
1856
          vnmult_target = get_vnewk (vnmult(1), decrease)
×
1857
       end if
1858

1859
       call init_lorentz (vnmult_target)
×
1860
       call init_lorentz_conserve
×
1861
    end if
1862

1863
  contains
1864
    !> FIXME : Add documentation
1865
    pure real function get_vnewk (vnm, incr) result(vnm_target)
×
1866
      implicit none
1867
      logical, intent (in) :: incr
1868
      real, intent (in) :: vnm
1869
      if (incr) then
×
1870
         vnm_target = vnm * vnfac
×
1871
      else
1872
         vnm_target =  vnm * vnslow
×
1873
      end if
1874
    end function get_vnewk
×
1875
  end subroutine adjust_vnmult
1876

1877
  !> FIXME : Add documentation
1878
  subroutine init_ediffuse (vnmult_target)
×
1879
    use le_grids, only: negrid, forbid, ixi_to_il, speed => speed_maxw
1880
    use gs2_layouts, only: le_lo, e_lo, il_idx
1881
    use gs2_layouts, only: ig_idx, it_idx, ik_idx, is_idx
1882
    use array_utils, only: zero_array
1883
    implicit none
1884
    real, intent (in), optional :: vnmult_target
1885
    integer :: ie, is, ik, il, ig, it, ile, ixi, ielo
1886
    real, dimension (:), allocatable :: aa, bb, cc, xe
×
1887

1888
    allocate (aa(negrid), bb(negrid), cc(negrid), xe(negrid))
×
1889

1890
    ! want to use x variables instead of e because we want conservative form
1891
    ! for the x-integration
1892
    xe = speed
×
1893

1894
    if (present(vnmult_target)) then
×
1895
       vnmult(2) = max (vnmult_target, 1.0)
×
1896
    end if
1897

1898
    if(use_le_layout) then
×
1899

1900
       if (.not.allocated(ec1le)) then
×
1901
          allocate (ec1le   (nxi_lim, negrid, le_lo%llim_proc:le_lo%ulim_alloc))
×
1902
          allocate (ebetaale(nxi_lim, negrid, le_lo%llim_proc:le_lo%ulim_alloc))
×
1903
          allocate (eqle    (nxi_lim, negrid, le_lo%llim_proc:le_lo%ulim_alloc))
×
1904
          vnmult(2) = max(1.0, vnmult(2))
×
1905
       end if
1906
       call zero_array(ec1le)
×
1907
       call zero_array(ebetaale)
×
1908
       call zero_array(eqle)
×
1909

1910
       !$OMP PARALLEL DO DEFAULT(none) &
1911
       !$OMP PRIVATE(ile, ik, it, is, ig, ie, ixi, il, aa, bb, cc) &
1912
       !$OMP SHARED(le_lo, nxi_lim, forbid, ec1le, ebetaale, negrid, eqle, ixi_to_il, xe) &
1913
       !$OMP SCHEDULE(static)
1914
       do ile = le_lo%llim_proc, le_lo%ulim_proc
×
1915
          ik = ik_idx(le_lo, ile)
×
1916
          it = it_idx(le_lo, ile)
×
1917
          is = is_idx(le_lo, ile)
×
1918
          ig = ig_idx(le_lo, ile)
×
1919
          do ixi = 1, nxi_lim
×
1920
             il = ixi_to_il(ixi, ig)
×
1921
             if (forbid(ig, il)) cycle
×
1922
             call get_ediffuse_matrix (aa, bb, cc, ig, ik, it, il, is, xe)
×
1923
             ec1le(ixi, :, ile) = cc
×
1924
             ebetaale(ixi, 1, ile) = 1.0 / bb(1)
×
1925
             do ie = 1,  negrid - 1
×
1926
                eqle(ixi, ie+1, ile) = aa(ie+1) * ebetaale(ixi, ie, ile)
×
1927
                ebetaale(ixi, ie+1, ile) = 1.0 / ( bb(ie+1) - eqle(ixi, ie+1, ile) &
×
1928
                     * ec1le(ixi, ie, ile) )
×
1929
             end do
1930
          end do
1931
       end do
1932
       !$OMP END PARALLEL DO
1933
    else
1934

1935
       if (.not.allocated(ec1)) then
×
1936
          allocate (ec1    (negrid,e_lo%llim_proc:e_lo%ulim_alloc))
×
1937
          allocate (ebetaa (negrid,e_lo%llim_proc:e_lo%ulim_alloc))
×
1938
          allocate (eql    (negrid,e_lo%llim_proc:e_lo%ulim_alloc))
×
1939
          vnmult(2) = max(1.0, vnmult(2))
×
1940
       endif
1941
       call zero_array(ec1)
×
1942
       call zero_array(ebetaa)
×
1943
       call zero_array(eql)
×
1944

1945
       !$OMP PARALLEL DO DEFAULT(none) &
1946
       !$OMP PRIVATE(ielo, ik, it, is, ig, ie, ixi, il, aa, bb, cc) &
1947
       !$OMP SHARED(e_lo, forbid, xe, ec1, ebetaa, negrid, eql) &
1948
       !$OMP SCHEDULE(static)
1949
       do ielo = e_lo%llim_proc, e_lo%ulim_proc
×
1950
          il = il_idx(e_lo, ielo)
×
1951
          ig = ig_idx(e_lo, ielo)
×
1952
          if (forbid(ig, il)) cycle
×
1953
          is = is_idx(e_lo, ielo)
×
1954
          ik = ik_idx(e_lo, ielo)
×
1955
          it = it_idx(e_lo, ielo)
×
1956
          call get_ediffuse_matrix (aa, bb, cc, ig, ik, it, il, is, xe)
×
1957
          ec1(:, ielo) = cc
×
1958

1959
          ! fill in the arrays for the tridiagonal
1960
          ebetaa(1, ielo) = 1.0 / bb(1)
×
1961
          do ie = 1, negrid - 1
×
1962
             eql(ie+1, ielo) = aa(ie+1) * ebetaa(ie, ielo)
×
1963
             ebetaa(ie+1, ielo) = 1.0 / (bb(ie+1) - eql(ie+1, ielo) * ec1(ie, ielo))
×
1964
          end do
1965
       end do
1966
       !$OMP END PARALLEL DO
1967
    end if
1968

1969
    deallocate(aa, bb, cc, xe)
×
1970

1971
  end subroutine init_ediffuse
×
1972

1973
  !> FIXME : Add documentation
1974
  subroutine get_ediffuse_matrix (aa, bb, cc, ig, ik, it, il, is, xe)
×
1975
    use species, only: spec
1976
    use theta_grid, only: bmag
1977
    use le_grids, only: al, negrid, w=>w_maxw
1978
    use kt_grids, only: kperp2
1979
    use gs2_time, only: code_dt, tunits
1980

1981
    implicit none
1982

1983
    integer, intent (in) :: ig, ik, it, il, is
1984
    real, dimension (:), intent (in) :: xe
1985
    real, dimension (:), intent (out) :: aa, bb, cc
1986

1987
    integer :: ie
1988
    real :: vn, slb1, xe0, xe1, xe2, xel, xer, capgr, capgl, ee
1989

1990
    vn = vnmult(2) * spec(is)%vnewk * tunits(ik)
×
1991

1992
    slb1 = safe_sqrt(1.0 - bmag(ig)*al(il))     ! xi_j
×
1993

1994
    select case (ediff_switch)
×
1995
    case (ediff_scheme_default)
1996
       do ie = 2, negrid - 1
×
1997
          xe0 = xe(ie-1)
×
1998
          xe1 = xe(ie)
×
1999
          xe2 = xe(ie+1)
×
2000

2001
          xel = (xe0 + xe1) * 0.5
×
2002
          xer = (xe1 + xe2) * 0.5
×
2003

2004
          capgr = capg(xer)
×
2005
          capgl = capg(xel)
×
2006

2007
          ee = 0.125 * (1 - slb1**2) * vnew_s(ik,ie,is) * kperp2(ig,it,ik)*cfac &
×
2008
               / (bmag(ig) * spec(is)%zstm)**2
×
2009

2010
          ! coefficients for tridiagonal matrix:
2011
          cc(ie) = -0.25 * vn * code_dt * capgr / (w(ie) * (xe2 - xe1))
×
2012
          aa(ie) = -0.25 * vn * code_dt * capgl / (w(ie) * (xe1 - xe0))
×
2013
          bb(ie) = 1.0 - (aa(ie) + cc(ie)) + ee*code_dt
×
2014
       end do
2015

2016
       ! boundary at v = 0
2017
       xe1 = xe(1)
×
2018
       xe2 = xe(2)
×
2019
       xer = (xe1 + xe2) * 0.5
×
2020

2021
       capgr = capg(xer)
×
2022

2023
       ee = 0.125 * (1 - slb1**2) * vnew_s(ik,1,is) * kperp2(ig,it,ik)*cfac &
×
2024
            / (bmag(ig) * spec(is)%zstm)**2
×
2025

2026
       cc(1) = -0.25 * vn * code_dt * capgr / (w(1) * (xe2 - xe1))
×
2027
       aa(1) = 0.0
×
2028
       bb(1) = 1.0 - cc(1) + ee * code_dt
×
2029

2030
       ! boundary at v = infinity
2031
       xe0 = xe(negrid-1)
×
2032
       xe1 = xe(negrid)
×
2033

2034
       xel = (xe1 + xe0) * 0.5
×
2035

2036
       capgl = capg(xel)
×
2037

2038
       ee = 0.125 * (1.-slb1**2) * vnew_s(ik,negrid,is) * kperp2(ig,it,ik) * cfac &
×
2039
            / (bmag(ig) * spec(is)%zstm)**2
×
2040

2041
       cc(negrid) = 0.0
×
2042
       aa(negrid) = -0.25 * vn * code_dt * capgl / (w(negrid) * (xe1 - xe0))
×
2043
       bb(negrid) = 1.0 - aa(negrid) + ee*code_dt
×
2044

2045
    case (ediff_scheme_old)
2046

2047
       ! non-conservative scheme
2048
       do ie = 2, negrid-1
×
2049
          xe0 = xe(ie-1)
×
2050
          xe1 = xe(ie)
×
2051
          xe2 = xe(ie+1)
×
2052

2053
          xel = (xe0 + xe1) * 0.5
×
2054
          xer = (xe1 + xe2) * 0.5
×
2055

2056
          capgr = capg_old(xe1, xer)
×
2057
          capgl = capg_old(xe1, xel)
×
2058

2059
          ee = 0.125 * (1 - slb1**2) * vnew_s(ik,ie,is) * kperp2(ig,it,ik) * cfac &
×
2060
               / (bmag(ig) * spec(is)%zstm)**2
×
2061

2062
          ! coefficients for tridiagonal matrix:
2063
          cc(ie) = -vn * code_dt * capgr / ((xer-xel) * (xe2 - xe1))
×
2064
          aa(ie) = -vn * code_dt * capgl / ((xer-xel) * (xe1 - xe0))
×
2065
          bb(ie) = 1.0 - (aa(ie) + cc(ie)) + ee * code_dt
×
2066
       end do
2067

2068
       ! boundary at xe = 0
2069
       xe1 = xe(1)
×
2070
       xe2 = xe(2)
×
2071
       xer = (xe1 + xe2) * 0.5
×
2072

2073
       capgr = capg_old(xe1, xer)
×
2074

2075
       ee = 0.125 * (1 - slb1**2) * vnew_s(ik,1,is) * kperp2(ig,it,ik)*cfac &
×
2076
            / (bmag(ig) * spec(is)%zstm)**2
×
2077

2078
       cc(1) = -vn * code_dt * capgr / (xer * (xe2 - xe1))
×
2079
       aa(1) = 0.0
×
2080
       bb(1) = 1.0 - cc(1) + ee * code_dt
×
2081

2082
       ! boundary at xe = 1
2083
       xe0 = xe(negrid-1)
×
2084
       xe1 = xe(negrid)
×
2085
       xel = (xe1 + xe0) * 0.5
×
2086

2087
       capgl = capg_old(xe1, xel)
×
2088

2089
       ee = 0.125 * (1 - slb1**2) * vnew_s(ik,negrid,is) * kperp2(ig,it,ik)*cfac &
×
2090
            / (bmag(ig) * spec(is)%zstm)**2
×
2091

2092
       cc(negrid) = 0.0
×
2093
       aa(negrid) = -vn * code_dt * capgl / ((1.0-xel) * (xe1 - xe0))
×
2094
       bb(negrid) = 1.0 - aa(negrid) + ee * code_dt
×
2095
    end select
2096

2097
  contains
2098
    elemental real function capg_kernel(xe)
×
2099
      use constants, only: sqrt_pi
2100
      real, intent(in) :: xe
2101
      ! This is the same as hsg_func(xe) * 2 * xe**2
2102
      capg_kernel =  erf(xe) - 2 * xe * exp(-xe**2) / sqrt_pi
×
2103
    end function capg_kernel
×
2104

2105
    elemental real function capg(xe)
×
2106
      use constants, only: sqrt_pi
2107
      real, intent(in) :: xe
2108
      capg = 2 * exp(-xe**2) * capg_kernel(xe) / (xe * sqrt_pi)
×
2109
    end function capg
×
2110

2111
    elemental real function capg_old(xeA, xeB)
×
2112
      real, intent(in) :: xeA, xeB
2113
      capg_old = (0.5 * exp(xeA**2-xeB**2) / xeA**2) * capg_kernel(xeB) / xeB
×
2114
    end function capg_old
×
2115
  end subroutine get_ediffuse_matrix
2116

2117
  !> FIXME : Add documentation
2118
  subroutine init_lorentz (vnmult_target)
×
2119
    use le_grids, only: negrid, jend, ng2, nlambda, al, il_is_passing, il_is_wfb
2120
    use le_grids, only: setup_trapped_lambda_grids_old_finite_difference, setup_passing_lambda_grids
2121
    use gs2_layouts, only: ig_idx, ik_idx, ie_idx, is_idx, it_idx, lz_lo, le_lo
2122
    use theta_grid, only: ntgrid
2123
    use species, only: has_hybrid_electron_species, spec
2124
    use array_utils, only: zero_array
2125
    implicit none
2126
    real, intent (in), optional :: vnmult_target
2127
    integer :: ig, ixi, it, ik, ie, is, je, te2, ile, ilz
2128
    real, dimension (:), allocatable :: aa, bb, cc, dd, hh
×
2129
    real, dimension (:, :), allocatable :: local_weights
×
2130
    allocate (aa(nxi_lim), bb(nxi_lim), cc(nxi_lim), dd(nxi_lim), hh(nxi_lim))
×
2131

2132
    if (.not. allocated(pitch_weights)) then
×
2133
       ! Start by just copying the existing weights
2134
       allocate(local_weights(-ntgrid:ntgrid, nlambda))
×
2135
       local_weights = 0.0
×
2136
       ! We have to recaculate the passing weights here as when Radau-Gauss
2137
       ! grids are used the WFB (il=ng2+1) is treated as both passing and
2138
       ! trapped. Otherwise we could just copy the passing weights from wl.
2139
       call setup_passing_lambda_grids(al, local_weights)
×
2140
       call setup_trapped_lambda_grids_old_finite_difference(al, local_weights)
×
2141
       allocate(pitch_weights(nlambda, -ntgrid:ntgrid))
×
2142
       pitch_weights = transpose(local_weights)
×
2143
    end if
2144

2145
    call init_vpdiff
×
2146

2147
    if(use_le_layout) then
×
2148
       if (.not.allocated(c1le)) then
×
2149
          allocate (c1le    (nxi_lim, negrid, le_lo%llim_proc:le_lo%ulim_alloc))
×
2150
          allocate (betaale (nxi_lim, negrid, le_lo%llim_proc:le_lo%ulim_alloc))
×
2151
          allocate (qle     (nxi_lim, negrid, le_lo%llim_proc:le_lo%ulim_alloc))
×
2152
          if (heating) then
×
2153
             allocate (d1le    (nxi_lim, negrid, le_lo%llim_proc:le_lo%ulim_alloc))
×
2154
             allocate (h1le    (nxi_lim, negrid, le_lo%llim_proc:le_lo%ulim_alloc))
×
2155
             call zero_array(d1le)
×
2156
             call zero_array(h1le)
×
2157
          end if
2158
          vnmult(1) = max(1.0, vnmult(1))
×
2159
       endif
2160
       call zero_array(c1le)
×
2161
       call zero_array(betaale)
×
2162
       call zero_array(qle)
×
2163

2164
       if (present(vnmult_target)) then
×
2165
          vnmult(1) = max (vnmult_target, 1.0)
×
2166
       end if
2167

2168
       !$OMP PARALLEL DO DEFAULT(none) &
2169
       !$OMP PRIVATE(ile, ik, it, is, ig, je, te2, ie, aa, bb, cc, dd, hh, ixi) &
2170
       !$OMP SHARED(le_lo, jend, special_wfb_lorentz, ng2, negrid, spec, c1le, &
2171
       !$OMP d1le, h1le, qle, betaale) &
2172
       !$OMP SCHEDULE(static)
2173
       do ile = le_lo%llim_proc, le_lo%ulim_proc
×
2174
          ig = ig_idx(le_lo,ile)
×
2175
          ik = ik_idx(le_lo,ile)
×
2176
          it = it_idx(le_lo,ile)
×
2177
          is = is_idx(le_lo,ile)
×
2178
          je = jend(ig)
×
2179
          !if (je <= ng2+1) then
2180
          ! MRH the above line is likely the original cause of the wfb bug in collisions
2181
          ! by treating collisions arrays here as if there are only passing particles
2182
          ! when there are only passing particles plus wfb (and no other trapped particles)
2183
          ! introduced an inconsistency in the tri-diagonal solve for theta =+/- pi
2184
          ! Fixed below.
2185
          ! Here te2 is the total number of unique valid xi at this point.
2186
          ! For passing particles this is 2*ng2 whilst for trapped we subtract one
2187
          ! from the number of valid pitch angles due to the "degenerate" duplicate
2188
          ! vpar = 0 point.
2189
          if (il_is_passing(je) .or. (il_is_wfb(je) .and. special_wfb_lorentz) ) then ! MRH
×
2190
             te2 = 2 * ng2
×
2191
          else
2192
             te2 = 2 * je - 1
×
2193
          end if
2194
          do ie = 1, negrid
×
2195

2196
             call get_lorentz_matrix (aa, bb, cc, dd, hh, ig, ik, it, ie, is)
×
2197

2198
             if (has_hybrid_electron_species(spec)) &
×
2199
                  call set_hzero_lorentz_collisions_matrix(aa, bb, cc, je, ik, is)
×
2200

2201
             c1le(:, ie, ile) = cc
×
2202
             if (allocated(d1le)) then
×
2203
                d1le(:, ie, ile) = dd
×
2204
                h1le(:, ie, ile) = hh
×
2205
             end if
2206

2207
             qle(1, ie, ile) = 0.0
×
2208
             betaale(1, ie, ile) = 1.0 / bb(1)
×
2209
             do ixi = 1, te2-1
×
2210
                qle(ixi+1, ie, ile) = aa(ixi+1) * betaale(ixi, ie, ile)
×
2211
                betaale(ixi+1, ie, ile) = 1.0 / ( bb(ixi+1) - qle(ixi+1, ie, ile) &
×
2212
                     * c1le(ixi, ie, ile) )
×
2213
             end do
2214
             qle(te2+1:, ie, ile) = 0.0
×
2215
             betaale(te2+1:, ie, ile) = 0.0
×
2216
          end do
2217
       end do
2218
       !$OMP END PARALLEL DO
2219
    else
2220

2221
       if (.not.allocated(c1)) then
×
2222
          allocate (c1(nxi_lim,lz_lo%llim_proc:lz_lo%ulim_alloc))
×
2223
          allocate (betaa(nxi_lim,lz_lo%llim_proc:lz_lo%ulim_alloc))
×
2224
          allocate (ql(nxi_lim,lz_lo%llim_proc:lz_lo%ulim_alloc))
×
2225
          if (heating) then
×
2226
             allocate (d1   (nxi_lim,lz_lo%llim_proc:lz_lo%ulim_alloc))
×
2227
             allocate (h1   (nxi_lim,lz_lo%llim_proc:lz_lo%ulim_alloc))
×
2228
             call zero_array(d1)
×
2229
             call zero_array(h1)
×
2230
          end if
2231
          vnmult(1) = max(1.0, vnmult(1))
×
2232
       end if
2233

2234
       call zero_array(c1)
×
2235
       call zero_array(betaa)
×
2236
       call zero_array(ql)
×
2237

2238
       if (present(vnmult_target)) then
×
2239
          vnmult(1) = max (vnmult_target, 1.0)
×
2240
       end if
2241

2242
       !$OMP PARALLEL DO DEFAULT(none) &
2243
       !$OMP PRIVATE(ilz, ik, it, is, ig, je, te2, ie, aa, bb, cc, dd, hh, ixi) &
2244
       !$OMP SHARED(lz_lo, jend, special_wfb_lorentz, spec, c1, d1, h1, betaa, ql, ng2) &
2245
       !$OMP SCHEDULE(static)
2246
       do ilz = lz_lo%llim_proc, lz_lo%ulim_proc
×
2247
          is = is_idx(lz_lo,ilz)
×
2248
          ik = ik_idx(lz_lo,ilz)
×
2249
          it = it_idx(lz_lo,ilz)
×
2250
          ie = ie_idx(lz_lo,ilz)
×
2251
          ig = ig_idx(lz_lo,ilz)
×
2252
          je = jend(ig)
×
2253
          !if (je <= ng2+1) then
2254
          ! MRH the above line is likely the original cause of the wfb bug in collisions
2255
          ! by treating collisions arrays here as if there are only passing particles
2256
          ! when there are only passing particles plus wfb (and no other trapped particles)
2257
          ! introduced an inconsistency in the tri-diagonal solve for theta =+/- pi
2258
          ! Fixed below
2259
          if (il_is_passing(je) .or. (il_is_wfb(je) .and. special_wfb_lorentz) ) then ! MRH
×
2260
             te2 = 2 * ng2
×
2261
          else
2262
             te2 = 2 * je - 1
×
2263
          end if
2264

2265
          call get_lorentz_matrix (aa, bb, cc, dd, hh, ig, ik, it, ie, is)
×
2266

2267
          if (has_hybrid_electron_species(spec)) &
×
2268
               call set_hzero_lorentz_collisions_matrix(aa, bb, cc, je, ik, is)
×
2269

2270
          c1(:, ilz) = cc
×
2271
          if (allocated(d1)) then
×
2272
             d1(:, ilz) = dd
×
2273
             h1(:, ilz) = hh
×
2274
          end if
2275

2276
          ql(1, ilz) = 0.0
×
2277
          betaa(1, ilz) = 1.0 / bb(1)
×
2278
          do ixi = 1, te2-1
×
2279
             ql(ixi+1, ilz) = aa(ixi+1) * betaa(ixi, ilz)
×
2280
             betaa(ixi+1, ilz) = 1.0 / (bb(ixi+1) - ql(ixi+1, ilz) * c1(ixi, ilz))
×
2281
          end do
2282
          ql(te2+1:, ilz) = 0.0
×
2283
          c1(te2+1:, ilz) = 0.0
×
2284
          betaa(te2+1:, ilz) = 0.0
×
2285
       end do
2286
       !$OMP END PARALLEL DO
2287
    end if
2288

2289
    deallocate (aa, bb, cc, dd, hh)
×
2290

2291
  end subroutine init_lorentz
×
2292

2293
  !> Special behaviour when h=0 for passing non-zonal electrons
2294
  !>
2295
  !> The effect of these changes is to exclude passing electrons
2296
  !> From pitch angle scattering, and to enforce 0 passing as a
2297
  !> boundary condition For the trapped particle pitch angle
2298
  !> scattering.
2299
  subroutine set_hzero_lorentz_collisions_matrix(aa, bb, cc, je, ik, is)
×
2300
    use species, only: is_hybrid_electron_species, spec
2301
    use kt_grids, only: aky
2302
    use le_grids, only: ng2, grid_has_trapped_particles
2303
    use warning_helpers, only: is_not_zero
2304
    implicit none
2305
    real, dimension(:), intent(in out) :: aa, bb, cc
2306
    integer, intent(in) :: je, ik, is
2307
    !> il index of 1st non-wfb trapped particle
2308
    integer :: il_llim
2309
    !> il index of last non-wfb trapped particle
2310
    integer :: il_ulim
2311

2312
    il_llim = ng2 + 2
×
2313
    il_ulim = 2*je-1 - (ng2+ 1)
×
2314

2315
    ! If not trapped particles then need to adjust limits
2316
    ! Want to force aa = cc = 0 ; bb = 1
2317
    if (.not. grid_has_trapped_particles()) then
×
2318
       il_llim = ng2 + 1 ; il_ulim = ng2 -1
×
2319
    end if
2320

2321
    if ( is_hybrid_electron_species(spec(is)) .and. is_not_zero(aky(ik))) then
×
2322
       aa(:il_llim) = 0.
×
2323
       bb(:il_llim-1) = 1.
×
2324
       cc(:il_llim-1) = 0.
×
2325
       aa(il_ulim+1:) = 0.
×
2326
       bb(il_ulim+1:) = 1.
×
2327
       cc(il_ulim:) = 0.
×
2328
    endif
2329
  end subroutine set_hzero_lorentz_collisions_matrix
×
2330

2331
  !> FIXME : Add documentation
2332
  subroutine get_lorentz_matrix (aa, bb, cc, dd, hh, ig, ik, it, ie, is)
×
2333
    use species, only: spec
2334
    use le_grids, only: al, energy => energy_maxw, xi, ng2
2335
    use le_grids, only: jend, al, il_is_passing, il_is_wfb
2336
    use gs2_time, only: code_dt, tunits
2337
    use kt_grids, only: kperp2
2338
    use theta_grid, only: bmag
2339
    use warning_helpers, only: is_zero
2340
    implicit none
2341
    real, dimension (:), intent (out) :: aa, bb, cc, dd, hh
2342
    integer, intent (in) :: ig, ik, it, ie, is
2343
    integer :: il, je, te, te2, teh
2344
    real :: slb0, slb1, slb2, slbl, slbr, vhyp, vn, vnh, vnc, ee, deltaxi
2345

2346
    je = jend(ig)
×
2347
!
2348
!CMR, 17/2/2014:
2349
!         te, te2, teh: indices in xi, which runs from +1 -> -1.
2350
!         te   :  index of minimum xi value >= 0.
2351
!         te2  :  total #xi values = index of minimum xi value (= -1)
2352
!         teh  :  index of minimum xi value > 0.
2353
!                 teh = te if no bouncing particle at this location
2354
!              OR teh = te-1 if there is a bouncing particle
2355
!
2356
    if (il_is_passing(je) .or. (il_is_wfb(je) .and. special_wfb_lorentz)) then
×
2357
       !CMRDDGC, 17/2/2014:
2358
       !   This clause is appropriate for Lorentz collisons with
2359
       !         SPECIAL (unphysical) treatment of wfb at its bounce point
2360
       te = ng2
×
2361
       te2 = 2*ng2
×
2362
       teh = ng2
×
2363
    else
2364
       !CMRDDGC, 17/2/2014:
2365
       !   This clause is appropriate for Lorentz collisons with
2366
       !         STANDARD treatment of wfb at its bounce point
2367
       te = je
×
2368
       te2 = 2*je-1
×
2369
       teh = je-1
×
2370
    end if
2371

2372
    if (collision_model_switch == collision_model_lorentz_test) then
×
2373
       vn = vnmult(1) * abs(spec(is)%vnewk) * tunits(ik)
×
2374
       vnc = 0.
×
2375
       vnh = 0.
×
2376
    else
2377
       if (hyper_colls) then
×
2378
          vhyp = vnewh(ig, it, ik, is)
×
2379
       else
2380
          vhyp = 0.0
×
2381
       end if
2382
       ! vnc and vnh only needed when heating is true
2383
       vnc = vnmult(1) * vnew(ik, ie, is)
×
2384
       if (hypermult) then
×
2385
          vn = vnmult(1) * vnew(ik, ie, is) * (1 + vhyp)
×
2386
          vnh = vhyp * vnc
×
2387
       else
2388
          vn = vnmult(1) * vnew(ik, ie, is) + vhyp
×
2389
          vnh = vhyp
×
2390
       end if
2391
    end if
2392

2393
    aa = 0.0 ; bb = 0.0 ; cc = 0.0 ; dd = 0.0 ; hh = 0.0
×
2394

2395
    select case (lorentz_switch)
×
2396
    case (lorentz_scheme_default)
2397

2398
       do il = 2, te-1
×
2399
          slb0 = safe_sqrt(1.0 - bmag(ig) * al(il-1))
×
2400
          slb1 = safe_sqrt(1.0 - bmag(ig) * al(il))
×
2401
          slb2 = safe_sqrt(1.0 - bmag(ig) * al(il+1))
×
2402

2403
          slbl = (slb1 + slb0) * 0.5  ! xi(j-1/2)
×
2404
          slbr = (slb1 + slb2) * 0.5  ! xi(j+1/2)
×
2405

2406
          ee = 0.5 * energy(ie)*(1 + slb1**2) * kperp2(ig,it,ik) * cfac &
×
2407
               / (bmag(ig) * spec(is)%zstm)**2
×
2408

2409
          ! coefficients for tridiagonal matrix:
2410
          cc(il) = 2.0 * vn * code_dt * (1 - slbr**2) / &
×
2411
               (pitch_weights(il, ig) * (slb2 - slb1))
×
2412
          aa(il) = 2.0 * vn * code_dt * (1 - slbl**2) / &
×
2413
               (pitch_weights(il, ig) * (slb1 - slb0))
×
2414
          bb(il) = 1.0 - (aa(il) + cc(il)) + ee * vn * code_dt
×
2415

2416
          ! coefficients for entropy heating calculation
2417
          if (heating) then
×
2418
             dd(il) = vnc * (-2.0 * (1 - slbr**2) / &
×
2419
                  (pitch_weights(il, ig) * (slb2 - slb1)) + ee)
×
2420
             hh(il) = vnh * (-2.0 * (1 - slbr**2) / &
×
2421
                  (pitch_weights(il, ig) * (slb2 - slb1)) + ee)
×
2422
          end if
2423
       end do
2424

2425
       ! boundary at xi = 1
2426
       slb1 = safe_sqrt(1.0 - bmag(ig) * al(1))
×
2427
       slb2 = safe_sqrt(1.0 - bmag(ig) * al(2))
×
2428

2429
       slbr = (slb1 + slb2) * 0.5
×
2430

2431
       ee = 0.5 * energy(ie) * (1 + slb1**2) * kperp2(ig,it,ik) * cfac &
×
2432
            / (bmag(ig) * spec(is)%zstm)**2
×
2433

2434
       cc(1) = 2.0 * vn * code_dt * (1.0 - slbr**2) &
×
2435
            / (pitch_weights(1, ig) * (slb2 - slb1))
×
2436
       aa(1) = 0.0
×
2437
       bb(1) = 1.0 - cc(1) + ee * vn * code_dt
×
2438

2439
       if (heating) then
×
2440
          dd(1) = vnc * (-2.0 * (1 - slbr**2) &
×
2441
               / (pitch_weights(1, ig) * (slb2 - slb1)) + ee)
×
2442
          hh(1) = vnh * (-2.0 * (1 - slbr**2) &
×
2443
               / (pitch_weights(1, ig) * (slb2 - slb1)) + ee)
×
2444
       end if
2445

2446
       ! boundary at xi = 0
2447
       il = te
×
2448
       slb0 = safe_sqrt(1.0 - bmag(ig) * al(il-1))
×
2449
       if (te == ng2) then
×
2450
          slb1 = safe_sqrt(1.0 - bmag(ig) * al(il))
×
2451
          slb2 = -slb1
×
2452
       else
2453
          slb1 = 0.0
×
2454
          slb2 = -slb0
×
2455
       end if
2456

2457
       slbl = (slb1 + slb0) * 0.5
×
2458
       slbr = (slb1 + slb2) * 0.5
×
2459

2460
       ee = 0.5 * energy(ie) * (1 + slb1**2) * kperp2(ig,it,ik) * cfac &
×
2461
            / (bmag(ig) * spec(is)%zstm)**2
×
2462

2463
!CMR, 6/3/2014:
2464
! STANDARD treatment of pitch angle scattering must resolve T-P boundary.
2465
! NEED special_wfb= .false. to resolve T-P boundary at wfb bounce point
2466
!     (special_wfb= .true. AVOIDS TP boundary at wfb bounce point)
2467
!
2468
! Original code (pre-r2766) used eq.(42) Barnes et al, Phys Plasmas 16, 072107
2469
! (2009), with pitch angle weights to enhance accuracy in derivatives.
2470
! NB THIS FAILS at wfb bounce point, giving aa=cc=infinite,
2471
!    because weight wl(ig,il)=0 at wfb bounce point.
2472
!    UNPHYSICAL as d/dxi ((1-xi^2)g) IS NOT resolved numerically for wl=0.
2473
! MUST accept limitations of the grid resolution and USE FINITE coefficients!
2474
! FIX here by setting a FINITE width of the trapped region at wfb B-P
2475
!              deltaxi=xi(ig,ng2)-xi(ig,ng2+2)
2476
! ASIDE: NB    deltaxi=wl is actually 2*spacing in xi !!!
2477
!              which explains upfront factor 2 in definition of aa, cc
2478
       deltaxi = pitch_weights(il, ig)
×
2479
       if (te == je) deltaxi = 2 * deltaxi ! MRH vpar = 0 fix
×
2480
       ! MRH appropriate when endpoint (te) is a vpar = 0 point (je)
2481
       ! factor of 2 required above because xi=0 is a repeated point
2482
       ! on vpar > 0, vpar < 0 grids, and hence has half the weight associated
2483
       ! to it at each appearance compared to what the weight should be
2484
       ! as calculated in the continuum of points xi = (-1,1)
2485
       if ((.not. special_wfb_lorentz) .and. (is_zero(deltaxi)) .and. il_is_wfb(il)) then
×
2486
          deltaxi = xi(ng2, ig) - xi(ng2 + 2, ig)
×
2487
       endif
2488
       cc(il) = 2.0 * vn * code_dt * (1 - slbr**2) / (deltaxi * (slb2 - slb1))
×
2489
       aa(il) = 2.0 * vn * code_dt * (1 - slbl**2) / (deltaxi * (slb1 - slb0))
×
2490
       bb(il) = 1.0 - (aa(il) + cc(il)) + ee * vn * code_dt
×
2491

2492
       if (heating) then
×
2493
          dd(il) = vnc * (-2.0 * (1.0 - slbr**2) / (deltaxi * (slb2 - slb1)) + ee)
×
2494
          hh(il) = vnh * (-2.0 * (1.0 - slbr**2) / (deltaxi * (slb2 - slb1)) + ee)
×
2495
       end if
2496
!CMRend
2497

2498
    case (lorentz_scheme_old)
2499
       do il = 2, te-1
×
2500
          slb0 = safe_sqrt(1.0 - bmag(ig) * al(il-1))
×
2501
          slb1 = safe_sqrt(1.0 - bmag(ig) * al(il))
×
2502
          slb2 = safe_sqrt(1.0 - bmag(ig) * al(il+1))
×
2503

2504
          slbl = (slb1 + slb0) * 0.5  ! xi(j-1/2)
×
2505
          slbr = (slb1 + slb2) * 0.5  ! xi(j+1/2)
×
2506

2507
          ee = 0.5 * energy(ie) * (1 + slb1**2) * kperp2(ig,it,ik) * cfac &
×
2508
               / (bmag(ig) * spec(is)%zstm)**2
×
2509

2510
          ! coefficients for tridiagonal matrix:
2511
          cc(il) = -vn * code_dt * (1 - slbr**2) / ((slbr - slbl) * (slb2 - slb1))
×
2512
          aa(il) = -vn * code_dt * (1 - slbl**2) / ((slbr - slbl) * (slb1 - slb0))
×
2513
          bb(il) = 1.0 - (aa(il) + cc(il)) + ee * vn * code_dt
×
2514

2515
          ! coefficients for entropy heating calculation
2516
          if (heating) then
×
2517
             dd(il) = vnc * ((1 - slbr**2) / ((slbr - slbl) * (slb2 - slb1)) + ee)
×
2518
             hh(il) = vnh * ((1 - slbr**2) / ((slbr - slbl) * (slb2 - slb1)) + ee)
×
2519
          end if
2520
       end do
2521

2522
       ! boundary at xi = 1
2523
       slb0 = 1.0
×
2524
       slb1 = safe_sqrt(1.0 - bmag(ig) * al(1))
×
2525
       slb2 = safe_sqrt(1.0 - bmag(ig) * al(2))
×
2526

2527
       slbl = (slb1 + slb0) * 0.5
×
2528
       slbr = (slb1 + slb2) * 0.5
×
2529

2530
       ee = 0.5 * energy(ie) * (1 + slb1**2) * kperp2(ig,it,ik) * cfac &
×
2531
            / (bmag(ig) * spec(is)%zstm)**2
×
2532

2533
       cc(1) = -vn * code_dt * (-1.0 - slbr) / (slb2 - slb1)
×
2534
       aa(1) = 0.0
×
2535
       bb(1) = 1.0 - (aa(1) + cc(1)) + ee * vn * code_dt
×
2536

2537
       if (heating) then
×
2538
          dd(1) = vnc * ((1 - slbr**2) / ((slbr - slbl) * (slb2 - slb1)) + ee)
×
2539
          hh(1) = vnh * ((1 - slbr**2) / ((slbr - slbl) * (slb2 - slb1)) + ee)
×
2540
       end if
2541

2542
       ! boundary at xi = 0
2543
       il = te
×
2544
       slb0 = safe_sqrt(1.0 - bmag(ig) * al(il-1))
×
2545
       if (te == ng2) then
×
2546
          slb1 = safe_sqrt(1.0 - bmag(ig) * al(il))
×
2547
          slb2 = -slb1
×
2548
       else
2549
          slb1 = 0.0
×
2550
          slb2 = -slb0
×
2551
       end if
2552

2553
       slbl = (slb1 + slb0) * 0.5
×
2554
       slbr = (slb1 + slb2) * 0.5
×
2555

2556
       ee = 0.5 * energy(ie) * (1 + slb1**2) * kperp2(ig,it,ik) * cfac &
×
2557
            / (bmag(ig) * spec(is)%zstm)**2
×
2558

2559
       cc(il) = -vn * code_dt * (1 - slbr**2) / ((slbr - slbl) * (slb2 - slb1))
×
2560
       aa(il) = -vn * code_dt * (1 - slbl**2) / ((slbr - slbl) * (slb1 - slb0))
×
2561
       bb(il) = 1.0 - (aa(il) + cc(il)) + ee * vn * code_dt
×
2562

2563
       if (heating) then
×
2564
          dd(il) = vnc * ((1 - slbr**2) / ((slbr - slbl) * (slb2 - slb1)) + ee)
×
2565
          hh(il) = vnh * ((1 - slbr**2) / ((slbr - slbl) * (slb2 - slb1)) + ee)
×
2566
       end if
2567

2568
    end select
2569

2570
    ! assuming symmetry in xi, fill in the rest of the arrays.
2571
    aa(te+1:te2) = cc(teh:1:-1)
×
2572
    bb(te+1:te2) = bb(teh:1:-1)
×
2573
    cc(te+1:te2) = aa(teh:1:-1)
×
2574

2575
    if (heating) then
×
2576
       dd(te+1:te2) = dd(teh:1:-1)
×
2577
       hh(te+1:te2) = hh(teh:1:-1)
×
2578
    end if
2579

2580
  end subroutine get_lorentz_matrix
×
2581

2582
  !> FIXME : Add documentation
2583
  !>
2584
  !> @note Currently this method doesn't use anything (aside from safe_sqrt)
2585
  !> from the collisions module so _could_ be moved to le_grids or diagnostics.
2586
  !> It _could_ potentially use get_lorentz_matrix so we'll leave here for now.
2587
  subroutine init_lorentz_error
×
2588
    use le_grids, only: jend, al, ng2, nlambda, &
2589
         il_is_wfb, il_is_passing, il_is_trapped
2590
    use theta_grid, only: ntgrid, bmag
2591
    use array_utils, only: zero_array
2592
    implicit none
2593
    
2594
    integer :: je, ig, il, ip, ij, im
2595
    real :: slb0, slb1, slb2, slbr, slbl
2596
    real, dimension (:), allocatable :: slb
×
2597
    real, dimension (:,:), allocatable :: dprod
×
2598
    real, dimension (:,:,:), allocatable :: dlcoef, d2lcoef
×
2599

2600
    allocate(slb(2*nlambda))
×
2601
    allocate (dprod(nlambda,5))
×
2602

2603
    allocate (dlcoef(-ntgrid:ntgrid,nlambda,5))
×
2604
    allocate (d2lcoef(-ntgrid:ntgrid,nlambda,5))
×
2605
    allocate (dtot(-ntgrid:ntgrid,nlambda,5))
×
2606
    allocate (fdf(-ntgrid:ntgrid,nlambda), fdb(-ntgrid:ntgrid,nlambda))
×
2607

2608
    dlcoef = 1.0; call zero_array(d2lcoef); call zero_array(dtot)
×
2609
    call zero_array(fdf); call zero_array(fdb); slb = 0.0
×
2610

2611
    ! This loop appears to be calculating aa and cc of get_lorentz_matrix
2612
    ! when using lorentz_scheme_old and storing fdb = aa/(-vn*code_dt),
2613
    ! fdf = cc/(-vn*code_dt). Given we don't use lorentz_scheme_old by
2614
    ! default is this diagnostic still applicable?
2615
    do ig=-ntgrid,ntgrid
×
2616
       je = jend(ig)
×
2617
       
2618
       if (il_is_passing(je) .or. il_is_wfb(je)) then            ! no trapped particles
×
2619

2620
! calculation of xi and finite difference coefficients for non-boundary points
2621
          do il=2,ng2-1
×
2622
             slb(il) = safe_sqrt(1.0-al(il)*bmag(ig))   ! xi_{j}
×
2623
             
2624
             slb2 = safe_sqrt(1.0-al(il+1)*bmag(ig))    ! xi_{j+1}
×
2625
             slb1 = slb(il)
×
2626
             slb0 = safe_sqrt(1.0-al(il-1)*bmag(ig))    ! xi_{j-1}
×
2627
             
2628
             slbr = (slb2+slb1)*0.5                     ! xi_{j+1/2}
×
2629
             slbl = (slb1+slb0)*0.5                     ! xi_{j-1/2}
×
2630

2631
! finite difference coefficients
2632
             fdf(ig,il) = (1.0 - slbr*slbr)/(slbr - slbl)/(slb2 - slb1)
×
2633
             fdb(ig,il) = (1.0 - slbl*slbl)/(slbr - slbl)/(slb1 - slb0)
×
2634
          end do
2635

2636
! boundary at xi = 1
2637
          slb(1) = safe_sqrt(1.0-al(1)*bmag(ig))
×
2638
          slb0 = 1.0
×
2639
          slb1 = slb(1)
×
2640
          slb2 = slb(2)
×
2641

2642
          slbl = (slb1 + slb0)/2.0
×
2643
          slbr = (slb1 + slb2)/2.0
×
2644

2645
! derivative of [(1-xi**2)*df/dxi] at xi_{j=1} is centered, with upper xi=1 and
2646
! lower xi = xi_{j+1/2}
2647
          fdf(ig,1) = (-1.0-slbr)/(slb2-slb1)
×
2648
          fdb(ig,1) = 0.0
×
2649

2650
! boundary at xi = 0
2651
          il = ng2
×
2652
          slb(il) = safe_sqrt(1.0 - al(il)*bmag(ig))
×
2653
          slb0 = safe_sqrt(1.0 - bmag(ig)*al(il-1))
×
2654
          slb1 = slb(il)
×
2655
          slb2 = -slb1
×
2656

2657
          slbl = (slb1 + slb0)/2.0
×
2658
          slbr = (slb1 + slb2)/2.0
×
2659

2660
          fdf(ig,il) = (1.0 - slbr*slbr)/(slbr-slbl)/(slb2-slb1)
×
2661
          fdb(ig,il) = (1.0 - slbl*slbl)/(slbr-slbl)/(slb1-slb0)
×
2662

2663
          slb(ng2+1:) = -slb(ng2:1:-1)
×
2664
       else          ! run with trapped particles
2665
          do il=2,je-1
×
2666
             slb(il) = safe_sqrt(1.0-al(il)*bmag(ig))
×
2667
             
2668
             slb2 = safe_sqrt(1.0-al(il+1)*bmag(ig))
×
2669
             slb1 = slb(il)
×
2670
             slb0 = safe_sqrt(1.0-al(il-1)*bmag(ig))
×
2671
             
2672
             slbr = (slb2+slb1)*0.5
×
2673
             slbl = (slb1+slb0)*0.5
×
2674

2675
             fdf(ig,il) = (1.0 - slbr*slbr)/(slbr - slbl)/(slb2 - slb1)
×
2676
             fdb(ig,il) = (1.0 - slbl*slbl)/(slbr - slbl)/(slb1 - slb0)
×
2677
          end do
2678

2679
! boundary at xi = 1
2680
          slb(1) = safe_sqrt(1.0-bmag(ig)*al(1))
×
2681
          slb0 = 1.0
×
2682
          slb1 = slb(1)
×
2683
          slb2 = slb(2)
×
2684

2685
          slbr = (slb1 + slb2)/2.0
×
2686

2687
          fdf(ig,1) = (-1.0 - slbr)/(slb2-slb1)
×
2688
          fdb(ig,1) = 0.0
×
2689

2690
! boundary at xi = 0
2691
          il = je
×
2692
          slb(il) = safe_sqrt(1.0-bmag(ig)*al(il))
×
2693
          slb0 = slb(je-1)
×
2694
          slb1 = 0.
×
2695
          slb2 = -slb0                                                        
×
2696

2697
          slbl = (slb1 + slb0)/2.0
×
2698

2699
          fdf(ig,il) = (1.0 - slbl*slbl)/slb0/slb0
×
2700
          fdb(ig,il) = fdf(ig,il)
×
2701

2702
          slb(je+1:2*je-1) = -slb(je-1:1:-1)
×
2703
       end if
2704

2705
! compute coefficients (dlcoef) multipyling first derivative of h
2706
       do il=3,ng2
×
2707
          do ip=il-2,il+2
×
2708
             if (il == ip) then
×
2709
                dlcoef(ig,il,ip-il+3) = 0.0
×
2710
                do ij=il-2,il+2
×
2711
                   if (ij /= ip) dlcoef(ig,il,ip-il+3) = dlcoef(ig,il,ip-il+3) + 1/(slb(il)-slb(ij))
×
2712
                end do
2713
             else
2714
                do ij=il-2,il+2
×
2715
                   if (ij /= ip .and. ij /= il) then
×
2716
                      dlcoef(ig,il,ip-il+3) = dlcoef(ig,il,ip-il+3)*(slb(il)-slb(ij))/(slb(ip)-slb(ij))
×
2717
                   end if
2718
                end do
2719
                dlcoef(ig,il,ip-il+3) = dlcoef(ig,il,ip-il+3)/(slb(ip)-slb(il))
×
2720
             end if
2721
             dlcoef(ig,il,ip-il+3) = -2.0*slb(il)*dlcoef(ig,il,ip-il+3)
×
2722
          end do
2723
       end do
2724

2725
       il = 1
×
2726
       do ip=il,il+2
×
2727
          if (il == ip) then
×
2728
             dlcoef(ig,il,ip) = 0.0
×
2729
             do ij=il,il+2
×
2730
                if (ij /= ip) dlcoef(ig,il,ip) = dlcoef(ig,il,ip) + 1./(slb(il)-slb(ij))
×
2731
             end do
2732
          else
2733
             do ij=il,il+2
×
2734
                if (ij /= ip .and. ij /= il) then
×
2735
                   dlcoef(ig,il,ip) = dlcoef(ig,il,ip)*(slb(il)-slb(ij))/(slb(ip)-slb(ij))
×
2736
                end if
2737
             end do
2738
             dlcoef(ig,il,ip) = dlcoef(ig,il,ip)/(slb(ip)-slb(il))
×
2739
          end if
2740
          dlcoef(ig,il,ip) = -2.0*slb(il)*dlcoef(ig,il,ip)
×
2741
       end do
2742

2743
       il = 2
×
2744
       do ip=il-1,il+1
×
2745
          if (il == ip) then
×
2746
             dlcoef(ig,il,ip-il+2) = 0.0
×
2747
             do ij=il-1,il+1
×
2748
                if (ij /= ip) dlcoef(ig,il,ip-il+2) = dlcoef(ig,il,ip-il+2) + 1/(slb(il)-slb(ij))
×
2749
             end do
2750
          else
2751
             do ij=il-1,il+1
×
2752
                if (ij /= ip .and. ij /= il) then
×
2753
                   dlcoef(ig,il,ip-il+2) = dlcoef(ig,il,ip-il+2)*(slb(il)-slb(ij))/(slb(ip)-slb(ij))
×
2754
                end if
2755
             end do
2756
             dlcoef(ig,il,ip-il+2) = dlcoef(ig,il,ip-il+2)/(slb(ip)-slb(il))
×
2757
          end if
2758
          dlcoef(ig,il,ip-il+2) = -2.0*slb(il)*dlcoef(ig,il,ip-il+2)
×
2759
       end do
2760

2761
       dprod = 2.0
×
2762

2763
! compute coefficients (d2lcoef) multiplying second derivative of h
2764
       do il=3,ng2
×
2765
          do ip=il-2,il+2
×
2766
             if (il == ip) then
×
2767
                do ij=il-2,il+2
×
2768
                   if (ij /= ip) then
×
2769
                      do im=il-2,il+2
×
2770
                         if (im /= ip .and. im /= ij) d2lcoef(ig,il,ip-il+3) = &
×
2771
                              d2lcoef(ig,il,ip-il+3) + 1./((slb(il)-slb(im))*(slb(il)-slb(ij)))
×
2772
                      end do
2773
                   end if
2774
                end do
2775
             else
2776
                do ij=il-2,il+2
×
2777
                   if (ij /= il .and. ij /= ip) then
×
2778
                      dprod(il,ip-il+3) = dprod(il,ip-il+3)*(slb(il)-slb(ij))/(slb(ip)-slb(ij))
×
2779
                   end if
2780
                end do
2781

2782
                do ij=il-2,il+2
×
2783
                   if (ij /= ip .and. ij /= il) then
×
2784
                      d2lcoef(ig,il,ip-il+3) = d2lcoef(ig,il,ip-il+3) + 1./(slb(il)-slb(ij))
×
2785
                   end if
2786
                end do
2787
                d2lcoef(ig,il,ip-il+3) = dprod(il,ip-il+3) &
×
2788
                     *d2lcoef(ig,il,ip-il+3)/(slb(ip)-slb(il))
×
2789
             end if
2790
             d2lcoef(ig,il,ip-il+3) = (1.0-slb(il)**2)*d2lcoef(ig,il,ip-il+3)
×
2791
          end do
2792
       end do
2793

2794
       il = 1
×
2795
       do ip=il,il+2
×
2796
          if (il == ip) then
×
2797
             do ij=il,il+2
×
2798
                if (ij /= ip) then
×
2799
                   do im=il,il+2
×
2800
                      if (im /= ip .and. im /= ij) d2lcoef(ig,il,ip) = d2lcoef(ig,il,ip) + 1./((slb(il)-slb(im))*(slb(il)-slb(ij)))
×
2801
                   end do
2802
                end if
2803
             end do
2804
          else
2805
             do ij=il,il+2
×
2806
                if (ij /= il .and. ij /= ip) then
×
2807
                   dprod(il,ip) = dprod(il,ip)*(slb(il)-slb(ij))/(slb(ip)-slb(ij))
×
2808
                end if
2809
             end do
2810

2811
             do ij=il,il+2
×
2812
                if (ij /= ip .and. ij /= il) then
×
2813
                   d2lcoef(ig,il,ip) = d2lcoef(ig,il,ip) + 1./(slb(il)-slb(ij))
×
2814
                end if
2815
             end do
2816
             d2lcoef(ig,il,ip) = dprod(il,ip)*d2lcoef(ig,il,ip)/(slb(ip)-slb(il))
×
2817
          end if
2818
          d2lcoef(ig,il,ip) = (1.0-slb(il)**2)*d2lcoef(ig,il,ip)
×
2819
       end do
2820

2821
       il = 2
×
2822
       do ip=il-1,il+1
×
2823
          if (il == ip) then
×
2824
             do ij=il-1,il+1
×
2825
                if (ij /= ip) then
×
2826
                   do im=il-1,il+1
×
2827
                      if (im /= ip .and. im /= ij) d2lcoef(ig,il,ip-il+2) &
×
2828
                           = d2lcoef(ig,il,ip-il+2) + 1./((slb(il)-slb(im))*(slb(il)-slb(ij)))
×
2829
                   end do
2830
                end if
2831
             end do
2832
          else
2833
             do ij=il-1,il+1
×
2834
                if (ij /= il .and. ij /= ip) then
×
2835
                   dprod(il,ip-il+2) = dprod(il,ip-il+2)*(slb(il)-slb(ij))/(slb(ip)-slb(ij))
×
2836
                end if
2837
             end do
2838

2839
             do ij=il-1,il+1
×
2840
                if (ij /= ip .and. ij /= il) then
×
2841
                   d2lcoef(ig,il,ip-il+2) = d2lcoef(ig,il,ip-il+2) + 1./(slb(il)-slb(ij))
×
2842
                end if
2843
             end do
2844
             d2lcoef(ig,il,ip-il+2) = dprod(il,ip-il+2)*d2lcoef(ig,il,ip-il+2)/(slb(ip)-slb(il))
×
2845
          end if
2846
          d2lcoef(ig,il,ip-il+2) = (1.0-slb(il)**2)*d2lcoef(ig,il,ip-il+2)
×
2847
       end do
2848
       
2849
       if (il_is_trapped(je)) then      ! have to handle trapped particles
×
2850

2851
          do il=ng2+1,je
×
2852
             do ip=il-2,il+2
×
2853
                if (il == ip) then
×
2854
                   dlcoef(ig,il,ip-il+3) = 0.0
×
2855
                   do ij=il-2,il+2
×
2856
                      if (ij /= ip) dlcoef(ig,il,ip-il+3) = dlcoef(ig,il,ip-il+3) + 1/(slb(il)-slb(ij))
×
2857
                   end do
2858
                else
2859
                   do ij=il-2,il+2
×
2860
                      if (ij /= ip .and. ij /= il) then
×
2861
                         dlcoef(ig,il,ip-il+3) = dlcoef(ig,il,ip-il+3)*(slb(il)-slb(ij))/(slb(ip)-slb(ij))
×
2862
                      end if
2863
                   end do
2864
                   dlcoef(ig,il,ip-il+3) = dlcoef(ig,il,ip-il+3)/(slb(ip)-slb(il))
×
2865
                end if
2866
                dlcoef(ig,il,ip-il+3) = -2.0*slb(il)*dlcoef(ig,il,ip-il+3)
×
2867
             end do
2868
          end do
2869

2870
          do il=ng2+1,je
×
2871
             do ip=il-2,il+2
×
2872
                if (il == ip) then
×
2873
                   do ij=il-2,il+2
×
2874
                      if (ij /= ip) then
×
2875
                         do im=il-2,il+2
×
2876
                            if (im /= ip .and. im /= ij) d2lcoef(ig,il,ip-il+3) = &
×
2877
                                 d2lcoef(ig,il,ip-il+3) + 1./((slb(il)-slb(im))*(slb(il)-slb(ij)))
×
2878
                         end do
2879
                      end if
2880
                   end do
2881
                else
2882
                   do ij=il-2,il+2
×
2883
                      if (ij /= il .and. ij /= ip) then
×
2884
                         dprod(il,ip-il+3) = dprod(il,ip-il+3)*(slb(il)-slb(ij))/(slb(ip)-slb(ij))
×
2885
                      end if
2886
                   end do
2887
                   
2888
                   do ij=il-2,il+2
×
2889
                      if (ij /= ip .and. ij /= il) then
×
2890
                         d2lcoef(ig,il,ip-il+3) = d2lcoef(ig,il,ip-il+3) + 1./(slb(il)-slb(ij))
×
2891
                      end if
2892
                   end do
2893
                   d2lcoef(ig,il,ip-il+3) = dprod(il,ip-il+3) &
×
2894
                        *d2lcoef(ig,il,ip-il+3)/(slb(ip)-slb(il))
×
2895
                end if
2896
                d2lcoef(ig,il,ip-il+3) = (1.0-slb(il)**2)*d2lcoef(ig,il,ip-il+3)
×
2897
             end do
2898
          end do
2899
          
2900
       end if
2901
    end do
2902

2903
    dtot = dlcoef + d2lcoef
×
2904

2905
    deallocate (slb, dprod, dlcoef, d2lcoef)
×
2906
  end subroutine init_lorentz_error
×
2907

2908
  !> FIXME : Add documentation  
2909
  subroutine solfp1_standard_layout (g, g1, diagnostics)
×
2910
    use gs2_layouts, only: g_lo, it_idx, ik_idx, ie_idx, is_idx
2911
    use theta_grid, only: ntgrid
2912
    use run_parameters, only: beta
2913
    use gs2_time, only: code_dt
2914
    use le_grids, only: energy => energy_maxw
2915
    use species, only: spec, is_electron_species
2916
    use dist_fn_arrays, only: vpa, aj0
2917
    use fields_arrays, only: aparnew
2918
    use run_parameters, only: ieqzip
2919
    use kt_grids, only: kwork_filter, kperp2
2920
    use optionals, only: get_option_with_default
2921
    implicit none
2922
    complex, dimension (-ntgrid:,:,g_lo%llim_proc:), intent (in out) :: g, g1
2923
    integer, optional, intent (in) :: diagnostics
2924
    integer :: isgn, it, ik, ie, is, iglo
2925

2926
    if (has_diffuse) then
×
2927
       call solfp_ediffuse_standard_layout (g)
×
2928
       if (conserve_moments) call conserve_diffuse (g, g1)
×
2929
    end if
2930

2931
    if (has_lorentz) then
×
2932
       if (drag) then
×
2933
          !$OMP PARALLEL DO DEFAULT(none) &
2934
          !$OMP PRIVATE(iglo, is, it, ik, ie, isgn) &
2935
          !$OMP SHARED(g_lo, spec, kwork_filter, g, ieqzip, vnmult, code_dt, vpa, &
2936
          !$OMP kperp2, aparnew, aj0, beta, energy) &
2937
          !$OMP SCHEDULE(static)
2938
          do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
2939
             is = is_idx(g_lo,iglo)
×
2940
             if (.not. is_electron_species(spec(is))) cycle
×
2941
             it = it_idx(g_lo,iglo)
×
2942
             ik = ik_idx(g_lo,iglo)
×
2943
             if(kwork_filter(it,ik)) cycle
×
2944
             if(ieqzip(it,ik)) cycle
×
2945
             ie = ie_idx(g_lo,iglo)
×
2946
             do isgn = 1, 2
×
2947
                g(:, isgn, iglo) = g(:, isgn, iglo) + vnmult(1)*spec(is)%vnewk*code_dt &
×
2948
                     * vpa(:,isgn,iglo)*kperp2(:,it,ik)*aparnew(:,it,ik)*aj0(:,iglo) &
×
2949
                     / ((-spec(is)%z*spec(is)%dens)*beta*spec(is)%stm*energy(ie)**1.5)
×
2950
                ! probably need 1/(spec(is_ion)%z*spec(is_ion)%dens) above
2951
                ! This has been implemented as 1/-spec(electron)%z*spec(electron)%dens
2952
                ! in an attempt handle the multi-ion species case.
2953
             end do
2954
          end do
2955
          !$OMP END PARALLEL DO
2956
       end if
2957

2958
       call solfp_lorentz_standard_layout (g, diagnostics)
×
2959
       if (conserve_moments) call conserve_lorentz (g, g1)
×
2960
    end if
2961
  end subroutine solfp1_standard_layout
×
2962

2963
  !> FIXME : Add documentation  
2964
  subroutine solfp1_le_layout (gle, diagnostics)
×
2965
    use gs2_layouts, only: le_lo, it_idx, ik_idx, ig_idx, is_idx
2966
    use run_parameters, only: beta, ieqzip
2967
    use gs2_time, only: code_dt
2968
    use le_grids, only: energy => energy_maxw, negrid
2969
    use species, only: spec, is_electron_species
2970
    use fields_arrays, only: aparnew
2971
    use kt_grids, only: kwork_filter, kperp2
2972
    implicit none
2973

2974
    complex, dimension (:,:,le_lo%llim_proc:), intent (in out) :: gle
2975
    integer, optional, intent (in) :: diagnostics
2976
    complex :: tmp
2977
    integer :: ig, it, ik, ie, is, ile, ixi
2978

2979
    if (has_diffuse) then
×
2980
       call solfp_ediffuse_le_layout (gle)
×
2981
       if (conserve_moments) call conserve_diffuse (gle)
×
2982
    end if
2983

2984
    if (has_lorentz) then
×
2985
       if (drag) then
×
2986
          !$OMP PARALLEL DO DEFAULT(none) &
2987
          !$OMP PRIVATE(ile, is, it, ik, ie, ig, ixi, tmp) &
2988
          !$OMP SHARED(le_lo, spec, kwork_filter, negrid, ieqzip, vnmult, code_dt, kperp2, &
2989
          !$OMP aparnew, beta, energy, nxi_lim, gle, vpa_aj0_le) &
2990
          !$OMP SCHEDULE(static)
2991
          do ile = le_lo%llim_proc, le_lo%ulim_proc
×
2992
             is = is_idx(le_lo,ile)
×
2993
             if (.not. is_electron_species(spec(is))) cycle
×
2994
             it = it_idx(le_lo,ile)
×
2995
             ik = ik_idx(le_lo,ile)
×
2996
             if(kwork_filter(it,ik)) cycle
×
2997
             if(ieqzip(it,ik)) cycle
×
2998
             ig = ig_idx(le_lo,ile)
×
2999
             do ie = 1, negrid
×
3000
                ! Note here we may need aparnew from {it, ik} not owned by this
3001
                ! processor in g_lo.
3002
                tmp = vnmult(1)*spec(is)%vnewk*code_dt &
×
3003
                     * kperp2(ig,it,ik)*aparnew(ig,it,ik) &
×
3004
                     / ((-spec(is)%z*spec(is)%dens)*beta*spec(is)%stm*energy(ie)**1.5)
×
3005
                do ixi = 1, nxi_lim
×
3006
                   gle(ixi, ie, ile) = gle(ixi, ie, ile) + tmp * vpa_aj0_le(ixi, ie, ile)
×
3007
                ! probably need 1/(spec(is_ion)%z*spec(is_ion)%dens) above
3008
                ! This has been implemented as 1/-spec(electron)%z*spec(electron)%dens
3009
                ! in an attempt handle the multi-ion species case.
3010
                end do
3011
             end do
3012
          end do
3013
          !$OMP END PARALLEL DO
3014
       end if
3015

3016
       call solfp_lorentz_le_layout (gle, diagnostics)
×
3017
       if (conserve_moments) call conserve_lorentz (gle)
×
3018
    end if
3019
  end subroutine solfp1_le_layout
×
3020

3021
  !> FIXME : Add documentation
3022
  subroutine conserve_lorentz_standard_layout (g, g1)
×
3023
    use theta_grid, only: ntgrid
3024
    use species, only: nspec
3025
    use kt_grids, only: naky, ntheta0, kwork_filter
3026
    use gs2_layouts, only: g_lo, ik_idx, it_idx, ie_idx, il_idx, is_idx
3027
    use le_grids, only: energy => energy_maxw, speed => speed_maxw, al, &
3028
         integrate_moment, negrid
3029
    use dist_fn_arrays, only: aj0, aj1, vpa
3030
    use run_parameters, only: ieqzip
3031
    use array_utils, only: copy
3032
    implicit none
3033
    complex, dimension (-ntgrid:,:,g_lo%llim_proc:), intent (in out) :: g, g1
3034
    complex, dimension (:,:,:), allocatable :: gtmp
×
3035
    real, dimension (:,:,:), allocatable :: vns
×
3036
    complex, dimension (:,:,:,:), allocatable :: v0y0, v1y1, v2y2
×
3037
    integer :: isgn, iglo, ik, ie, il, is, it
3038
    logical, parameter :: all_procs = .true.
3039

3040
    allocate (v0y0(-ntgrid:ntgrid, ntheta0, naky, nspec))
×
3041
    allocate (v1y1(-ntgrid:ntgrid, ntheta0, naky, nspec))
×
3042
    allocate (v2y2(-ntgrid:ntgrid, ntheta0, naky, nspec))
×
3043

3044
    allocate (gtmp(-ntgrid:ntgrid,2,g_lo%llim_proc:g_lo%ulim_alloc))
×
3045
    allocate (vns(naky,negrid,nspec))
×
3046
    vns = vnmult(1) * vnew_D
×
3047

3048
    if (drag) then
×
3049

3050
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3051
! First get v0y0
3052
       !$OMP PARALLEL DO DEFAULT(none) &
3053
       !$OMP PRIVATE(iglo, it, ik, isgn) &
3054
       !$OMP SHARED(g_lo, kwork_filter, gtmp, vpa, aj0, g) &
3055
       !$OMP SCHEDULE(static)
3056
       do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
3057
          it = it_idx(g_lo,iglo)
×
3058
          ik = ik_idx(g_lo,iglo)
×
3059
          if(kwork_filter(it,ik))cycle
×
3060
          do isgn = 1, 2
×
3061
             ! v0 = vpa J0 f0, y0 = g
3062
             gtmp(:, isgn, iglo) = vpa(:, isgn, iglo) * aj0(:, iglo) * g(:, isgn, iglo)
×
3063
          end do
3064
       end do
3065
       !$OMP END PARALLEL DO
3066
       call integrate_moment (gtmp, v0y0, all_procs)    ! v0y0
×
3067

3068
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3069
! Get y1 = y0 - v0y0 * z0 / (1 + v0z0)
3070
       !$OMP PARALLEL DO DEFAULT(none) &
3071
       !$OMP PRIVATE(iglo, it, ik, is, isgn) &
3072
       !$OMP SHARED(g_lo, kwork_filter, g1, g, ieqzip, v0y0, z0) &
3073
       !$OMP SCHEDULE(static)
3074
       do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
3075
          it = it_idx(g_lo,iglo)
×
3076
          ik = ik_idx(g_lo,iglo)
×
3077
          if(kwork_filter(it,ik)) cycle
×
3078
          if(ieqzip(it,ik)) cycle
×
3079
          is = is_idx(g_lo,iglo)
×
3080
          do isgn = 1, 2
×
3081
             g1(:, isgn, iglo) = g(:, isgn, iglo) - v0y0(:, it, ik, is) * z0(:, isgn, iglo)
×
3082
          end do
3083
       end do
3084
       !$OMP END PARALLEL DO
3085
    else
3086
       call copy(g, g1)
×
3087
    end if
3088

3089
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3090
! Now get v1y1
3091

3092
    !$OMP PARALLEL DO DEFAULT(none) &
3093
    !$OMP PRIVATE(iglo, it, ik, il, ie, is, isgn) &
3094
    !$OMP SHARED(g_lo, kwork_filter, conservative, gtmp, vns, speed, vpdiff, aj0, g1, vpa) &
3095
    !$OMP SCHEDULE(static)
3096
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
3097
       ik = ik_idx(g_lo,iglo)
×
3098
       it = it_idx(g_lo,iglo)
×
3099
       if(kwork_filter(it,ik))cycle
×
3100
       ie = ie_idx(g_lo,iglo)
×
3101
       is = is_idx(g_lo,iglo)
×
3102
       do isgn = 1, 2
×
3103
          ! v1 = nud vpa J0 f0, y1 = g1
3104
          if (conservative) then
×
3105
             il = il_idx(g_lo,iglo)
×
3106
             gtmp(:, isgn, iglo) = vns(ik, ie, is) * speed(ie) * vpdiff(:, isgn, il) &
×
3107
                  * aj0(:, iglo) * g1(:, isgn, iglo)
×
3108
          else
3109
             gtmp(:, isgn, iglo) = vns(ik, ie, is) * vpa(:, isgn, iglo) * aj0(:, iglo) &
×
3110
                  * g1(:, isgn, iglo)
×
3111
          end if
3112
       end do
3113
    end do
3114
    !$OMP END PARALLEL DO
3115

3116
    call integrate_moment (gtmp, v1y1, all_procs)    ! v1y1
×
3117

3118
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3119
! Get y2 = y1 - v1y1 * s1 / (1 + v1s1)
3120

3121
    !$OMP PARALLEL DO DEFAULT(none) &
3122
    !$OMP PRIVATE(iglo, it, ik, is, isgn) &
3123
    !$OMP SHARED(g_lo, kwork_filter, g1, ieqzip, v1y1, s0) &
3124
    !$OMP SCHEDULE(static)
3125
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
3126
       it = it_idx(g_lo,iglo)
×
3127
       ik = ik_idx(g_lo,iglo)
×
3128
       if(kwork_filter(it,ik)) cycle
×
3129
       if(ieqzip(it,ik)) cycle
×
3130
       is = is_idx(g_lo,iglo)
×
3131
       do isgn = 1, 2
×
3132
          g1(:, isgn, iglo) = g1(:, isgn, iglo) - v1y1(:, it, ik, is) * s0(:, isgn, iglo)
×
3133
       end do
3134
    end do
3135
    !$OMP END PARALLEL DO
3136

3137
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3138
! Now get v2y2
3139

3140
    !$OMP PARALLEL DO DEFAULT(none) &
3141
    !$OMP PRIVATE(iglo, it, ik, ie, il, is, isgn) &
3142
    !$OMP SHARED(g_lo, kwork_filter, gtmp, vns, energy, al, aj1, g1) &
3143
    !$OMP SCHEDULE(static)
3144
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
3145
       it = it_idx(g_lo,iglo)
×
3146
       ik = ik_idx(g_lo,iglo)
×
3147
       if(kwork_filter(it,ik))cycle
×
3148
       ie = ie_idx(g_lo,iglo)
×
3149
       il = il_idx(g_lo,iglo)
×
3150
       is = is_idx(g_lo,iglo)
×
3151
       do isgn = 1, 2
×
3152
          ! v2 = nud vperp J1 f0
3153
          gtmp(:, isgn, iglo) = vns(ik, ie, is) * energy(ie) * al(il) * aj1(:, iglo) &
×
3154
               * g1(:, isgn, iglo)
×
3155
       end do
3156
    end do
3157
    !$OMP END PARALLEL DO
3158

3159
    call integrate_moment (gtmp, v2y2, all_procs)    ! v2y2
×
3160

3161
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3162
! Finally get x = y2 - v2y2 * w2 / (1 + v2w2)
3163

3164
    !$OMP PARALLEL DO DEFAULT(none) &
3165
    !$OMP PRIVATE(iglo, it, ik, is, isgn) &
3166
    !$OMP SHARED(g_lo, kwork_filter, g, g1, ieqzip, v2y2, w0) &
3167
    !$OMP SCHEDULE(static)
3168
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
3169
       it = it_idx(g_lo,iglo)
×
3170
       ik = ik_idx(g_lo,iglo)
×
3171
       if(kwork_filter(it,ik)) cycle
×
3172
       if(ieqzip(it,ik)) cycle
×
3173
       is = is_idx(g_lo,iglo)
×
3174
       do isgn = 1, 2
×
3175
          g(:, isgn, iglo) = g1(:, isgn, iglo) - v2y2(:, it, ik, is) * w0(:, isgn, iglo)
×
3176
       end do
3177
    end do
3178
    !$OMP END PARALLEL DO
3179

3180
    deallocate (vns, v0y0, v1y1, v2y2, gtmp)
×
3181

3182
  end subroutine conserve_lorentz_standard_layout
×
3183

3184
  !> FIXME : Add documentation
3185
  subroutine conserve_lorentz_le_layout (gle)
×
3186
    use gs2_layouts, only: ik_idx, it_idx, ie_idx, il_idx, is_idx, ig_idx, le_lo
3187
    use le_grids, only: speed => speed_maxw, w, wxi, negrid
3188
    use run_parameters, only: ieqzip
3189
    use kt_grids, only: kwork_filter
3190
    implicit none
3191
    complex, dimension (:,:,le_lo%llim_proc:), intent (in out) :: gle
3192
    complex :: v0y0, v1y1, v2y2
3193
    real :: nud
3194
    integer :: ig, ik, ie, is, it, ile, ixi
3195

3196
    if (drag) then
×
3197
       !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3198
       ! First get v0y0 and y1 = y0 - v0y0 * z0 / (1 + v0z0)
3199

3200
       ! v0 = vpa J0 f0, y0 = gle
3201
       !$OMP PARALLEL DO DEFAULT(none) &
3202
       !$OMP PRIVATE(ile, it, ik, ixi, ie, is, ig, v0y0) &
3203
       !$OMP SHARED(le_lo, kwork_filter, ieqzip, negrid, nxi_lim, &
3204
       !$OMP z0le, w, wxi, vpa_aj0_le, gle) &
3205
       !$OMP SCHEDULE(static)
3206
       do ile = le_lo%llim_proc, le_lo%ulim_proc
×
3207
          it = it_idx(le_lo,ile)
×
3208
          ik = ik_idx(le_lo,ile)
×
3209
          if (kwork_filter(it, ik)) cycle
×
3210
          if (ieqzip(it, ik)) cycle
×
3211
          is = is_idx(le_lo, ile)
×
3212
          ig = ig_idx(le_lo, ile)
×
3213
          v0y0 = 0.0
×
3214
          do ie = 1, negrid
×
3215
             do ixi = 1, nxi_lim
×
3216
                ! Should we use vpdiff here if conservative?
3217
                v0y0 = v0y0 + vpa_aj0_le(ixi, ie, ile) * gle(ixi, ie, ile) * w(ie, is) * wxi(ixi, ig)
×
3218
             end do
3219
          end do
3220
          gle(:, :, ile) = gle(:, :, ile) - z0le(:, :, ile) * v0y0
×
3221
       end do
3222
       !$OMP END PARALLEL DO
3223
    end if
3224

3225
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3226
! Now get v1y1 and y2 = y1 - v1y1 * s1 / (1 + v1s1)
3227

3228
    ! v1 = nud vpa J0 f0, y1 = gle
3229
    if (conservative) then
×
3230
       !$OMP PARALLEL DO DEFAULT(none) &
3231
       !$OMP PRIVATE(ile, is, it, ik, ig, ixi, ie, nud, v1y1) &
3232
       !$OMP SHARED(le_lo, kwork_filter, ieqzip, negrid, nxi_lim, vpdiffle, &
3233
       !$OMP speed, vnmult, vnew_D, aj0le, gle, s0le, w, wxi) &
3234
       !$OMP SCHEDULE(static)
3235
       do ile = le_lo%llim_proc, le_lo%ulim_proc
×
3236
          ik = ik_idx(le_lo,ile)
×
3237
          it = it_idx(le_lo,ile)
×
3238
          if (kwork_filter(it, ik)) cycle
×
3239
          if (ieqzip(it, ik)) cycle
×
3240
          is = is_idx(le_lo,ile)
×
3241
          ig = ig_idx(le_lo,ile)
×
3242
          v1y1 = 0.0
×
3243
          do ie = 1, negrid
×
3244
             nud = speed(ie) * vnmult(1) * vnew_D(ik, ie, is) * w(ie, is)
×
3245
             do ixi = 1, nxi_lim
×
3246
                v1y1 = v1y1 + vpdiffle(ixi, ig) * nud * aj0le(ixi, ie, ile) * &
×
3247
                     gle(ixi, ie, ile) * wxi(ixi, ig)
×
3248
             end do
3249
          end do
3250
          gle(:, :, ile) = gle(:, :, ile) - s0le(:, :, ile) * v1y1
×
3251
       end do
3252
       !$OMP END PARALLEL DO
3253
    else
3254
       !$OMP PARALLEL DO DEFAULT(none) &
3255
       !$OMP PRIVATE(ile, is, it, ik, ig, ixi, ie, nud, v1y1) &
3256
       !$OMP SHARED(le_lo, kwork_filter, ieqzip, negrid, nxi_lim, vpa_aj0_le, vnmult, &
3257
       !$OMP vnew_D, gle, w, wxi, s0le) &
3258
       !$OMP SCHEDULE(static)
3259
       do ile = le_lo%llim_proc, le_lo%ulim_proc
×
3260
          ik = ik_idx(le_lo, ile)
×
3261
          it = it_idx(le_lo, ile)
×
3262
          if (kwork_filter(it, ik)) cycle
×
3263
          if (ieqzip(it, ik)) cycle
×
3264
          is = is_idx(le_lo, ile)
×
3265
          ig = ig_idx(le_lo, ile)
×
3266
          v1y1 = 0.0
×
3267
          do ie = 1, negrid
×
3268
             nud =  vnmult(1) * vnew_D(ik, ie, is) * w(ie, is)
×
3269
             do ixi = 1, nxi_lim
×
3270
                v1y1 = v1y1 + nud * vpa_aj0_le(ixi, ie, ile) * &
×
3271
                     gle(ixi, ie, ile) * wxi(ixi, ig)
×
3272
             end do
3273
          end do
3274
          gle(:, :, ile) = gle(:, :, ile) - s0le(:, :, ile) * v1y1
×
3275
       end do
3276
       !$OMP END PARALLEL DO
3277
    end if
3278

3279
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3280
    ! Now get v2y2 and x = y2 - v2y2 * w2 / (1 + v2w2)
3281

3282
    !$OMP PARALLEL DO DEFAULT(none) &
3283
    !$OMP PRIVATE(ile, it, ik, is, ie, ig, ixi, nud, v2y2) &
3284
    !$OMP SHARED(le_lo, kwork_filter, ieqzip, negrid, nxi_lim, &
3285
    !$OMP vnmult, vnew_D, vperp_aj1le, gle, w, wxi, w0le) &
3286
    !$OMP SCHEDULE(static)
3287
    do ile = le_lo%llim_proc, le_lo%ulim_proc
×
3288
       it = it_idx(le_lo, ile)
×
3289
       ik = ik_idx(le_lo, ile)
×
3290
       if (kwork_filter(it, ik)) cycle
×
3291
       if (ieqzip(it, ik)) cycle
×
3292
       is = is_idx(le_lo, ile)
×
3293
       ig = ig_idx(le_lo, ile)
×
3294
       v2y2 = 0.0
×
3295
       do ie = 1, negrid
×
3296
          nud = vnmult(1) * vnew_D(ik, ie, is) * w(ie, is)
×
3297
          do ixi = 1, nxi_lim
×
3298
             ! aj1vp2 = 2 * J1(arg)/arg * vperp^2
3299
             v2y2 = v2y2 + nud * vperp_aj1le(ixi, ie, ile) * gle(ixi, ie, ile) * wxi(ixi, ig)
×
3300
          end do
3301
       end do
3302
       gle(:, :, ile) = gle(:, :, ile) - w0le(:, :, ile) * v2y2
×
3303
    end do
3304
    !$OMP END PARALLEL DO
3305
  end subroutine conserve_lorentz_le_layout
×
3306

3307
  !> FIXME : Add documentation
3308
  subroutine conserve_diffuse_standard_layout (g, g1)
×
3309
    use theta_grid, only: ntgrid
3310
    use species, only: nspec
3311
    use kt_grids, only: naky, ntheta0, kwork_filter
3312
    use gs2_layouts, only: g_lo, ik_idx, it_idx, ie_idx, il_idx, is_idx
3313
    use le_grids, only: energy => energy_maxw, al, integrate_moment, negrid
3314
    use dist_fn_arrays, only: aj0, aj1, vpa
3315
    use run_parameters, only: ieqzip
3316
    use array_utils, only: zero_array
3317
    implicit none
3318
    complex, dimension (-ntgrid:,:,g_lo%llim_proc:), intent (in out) :: g, g1
3319
    complex, dimension (:,:,:), allocatable :: gtmp
×
3320
    real, dimension (:,:,:), allocatable :: vns
×
3321
    integer :: isgn, iglo, ik, ie, il, is, it 
3322
    complex, dimension (:,:,:,:), allocatable :: v0y0, v1y1, v2y2    
×
3323
    logical, parameter :: all_procs = .true.
3324

3325
    allocate (v0y0(-ntgrid:ntgrid, ntheta0, naky, nspec))
×
3326
    allocate (v1y1(-ntgrid:ntgrid, ntheta0, naky, nspec))
×
3327
    allocate (v2y2(-ntgrid:ntgrid, ntheta0, naky, nspec))
×
3328

3329
    allocate (vns(naky, negrid, nspec))
×
3330
    allocate (gtmp(-ntgrid:ntgrid, 2, g_lo%llim_proc:g_lo%ulim_alloc))
×
3331

3332
    vns = vnmult(2)*delvnew
×
3333

3334
    !This is needed to to ensure the it,ik values we don't set aren't included
3335
    !in the integral (can also be enforced in integrate_moment routine)
3336
    if(any(kwork_filter)) call zero_array(gtmp)
×
3337

3338
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3339
! First get v0y0
3340

3341
    !$OMP PARALLEL DO DEFAULT(none) &
3342
    !$OMP PRIVATE(iglo, it, ik, ie, is, isgn) &
3343
    !$OMP SHARED(g_lo, kwork_filter, gtmp, vnmult, vnew_E, aj0, g) &
3344
    !$OMP SCHEDULE(static)
3345
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
3346
       ik = ik_idx(g_lo,iglo)
×
3347
       it = it_idx(g_lo,iglo)
×
3348
       if(kwork_filter(it,ik))cycle
×
3349
       ie = ie_idx(g_lo,iglo)
×
3350
       is = is_idx(g_lo,iglo)
×
3351
       do isgn = 1, 2
×
3352
          ! v0 = nu_E E J0 f0
3353
          gtmp(:, isgn, iglo) = vnmult(2) * vnew_E(ik, ie, is) * aj0(:, iglo) &
×
3354
               * g(:, isgn, iglo)
×
3355
       end do
3356
    end do
3357
    !$OMP END PARALLEL DO
3358

3359
    call integrate_moment (gtmp, v0y0, all_procs)    ! v0y0
×
3360

3361
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3362
! Get y1 = y0 - v0y0 * z0 / (1 + v0z0)
3363

3364
    !$OMP PARALLEL DO DEFAULT(none) &
3365
    !$OMP PRIVATE(iglo, it, ik, is, isgn) &
3366
    !$OMP SHARED(g_lo, kwork_filter, g1, g, ieqzip, v0y0, bz0) &
3367
    !$OMP SCHEDULE(static)
3368
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
3369
       it = it_idx(g_lo,iglo)
×
3370
       ik = ik_idx(g_lo,iglo)
×
3371
       if(kwork_filter(it,ik)) cycle
×
3372
       if(ieqzip(it,ik)) cycle
×
3373
       is = is_idx(g_lo,iglo)
×
3374
       do isgn = 1, 2
×
3375
          g1(:, isgn, iglo) = g(:, isgn, iglo) - v0y0(:, it, ik, is) * bz0(:, isgn, iglo)
×
3376
       end do
3377
    end do
3378
    !$OMP END PARALLEL DO
3379

3380
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3381
! Now get v1y1
3382

3383
    !$OMP PARALLEL DO DEFAULT(none) &
3384
    !$OMP PRIVATE(iglo, it, ik, ie, is, isgn) &
3385
    !$OMP SHARED(g_lo, kwork_filter, gtmp, vns, vpa, aj0, g1) &
3386
    !$OMP SCHEDULE(static)
3387
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
3388
       ik = ik_idx(g_lo,iglo)
×
3389
       it = it_idx(g_lo,iglo)
×
3390
       if(kwork_filter(it,ik))cycle
×
3391
       ie = ie_idx(g_lo,iglo)
×
3392
       is = is_idx(g_lo,iglo)
×
3393
       do isgn = 1, 2
×
3394
          ! v1 = (nus-nud) vpa J0 f0
3395
          gtmp(:, isgn, iglo) = vns(ik, ie, is) * vpa(:, isgn, iglo) * aj0(:, iglo) &
×
3396
               * g1(:, isgn, iglo)
×
3397
       end do
3398
    end do
3399
    !$OMP END PARALLEL DO
3400

3401
    call integrate_moment (gtmp, v1y1, all_procs)    ! v1y1
×
3402

3403
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3404
! Get y2 = y1 - v1y1 * s1 / (1 + v1s1)
3405

3406
    !$OMP PARALLEL DO DEFAULT(none) &
3407
    !$OMP PRIVATE(iglo, it, ik, is, isgn) &
3408
    !$OMP SHARED(g_lo, kwork_filter, g1, ieqzip, v1y1, bs0) &
3409
    !$OMP SCHEDULE(static)
3410
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
3411
       it = it_idx(g_lo,iglo)
×
3412
       ik = ik_idx(g_lo,iglo)
×
3413
       if(kwork_filter(it,ik)) cycle
×
3414
       if(ieqzip(it,ik)) cycle
×
3415
       is = is_idx(g_lo,iglo)
×
3416
       do isgn = 1, 2
×
3417
          g1(:, isgn, iglo) = g1(:, isgn, iglo) - v1y1(:, it, ik, is) * bs0(:, isgn, iglo)
×
3418
       end do
3419
    end do
3420
    !$OMP END PARALLEL DO
3421

3422
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3423
! Now get v2y2
3424

3425
    !$OMP PARALLEL DO DEFAULT(none) &
3426
    !$OMP PRIVATE(iglo, it, ik, ie, il, is, isgn) &
3427
    !$OMP SHARED(g_lo, kwork_filter, gtmp, vns, energy, al, aj1, g1) &
3428
    !$OMP SCHEDULE(static)
3429
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
3430
       ik = ik_idx(g_lo,iglo)
×
3431
       it = it_idx(g_lo,iglo)
×
3432
       if(kwork_filter(it,ik))cycle
×
3433
       ie = ie_idx(g_lo,iglo)
×
3434
       il = il_idx(g_lo,iglo)
×
3435
       is = is_idx(g_lo,iglo)
×
3436
       do isgn = 1, 2
×
3437
          ! v2 = (nus-nud) vperp J1 f0
3438
          gtmp(:, isgn, iglo) = vns(ik, ie, is) * energy(ie) * al(il) * aj1(:, iglo) &
×
3439
               * g1(:, isgn, iglo)
×
3440
       end do
3441
    end do
3442
    !$OMP END PARALLEL DO
3443

3444
    call integrate_moment (gtmp, v2y2, all_procs)    ! v2y2
×
3445

3446
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3447
! Finally get x = y2 - v2y2 * w2 / (1 + v2w2)
3448

3449
    !$OMP PARALLEL DO DEFAULT(none) &
3450
    !$OMP PRIVATE(iglo, it, ik, is, isgn) &
3451
    !$OMP SHARED(g_lo, kwork_filter, g, g1, ieqzip, v2y2, bw0) &
3452
    !$OMP SCHEDULE(static)
3453
    do iglo = g_lo%llim_proc, g_lo%ulim_proc
×
3454
       it = it_idx(g_lo,iglo)
×
3455
       ik = ik_idx(g_lo,iglo)
×
3456
       if(kwork_filter(it,ik)) cycle
×
3457
       if(ieqzip(it,ik)) cycle
×
3458
       is = is_idx(g_lo,iglo)
×
3459
       do isgn = 1, 2
×
3460
          g(:, isgn, iglo) = g1(:, isgn, iglo) - v2y2(:, it, ik, is) * bw0(:, isgn, iglo)
×
3461
       end do
3462
    end do
3463
    !$OMP END PARALLEL DO
3464

3465
    deallocate (vns, v0y0, v1y1, v2y2, gtmp)
×
3466

3467
  end subroutine conserve_diffuse_standard_layout
×
3468

3469
  !> FIXME : Add documentation
3470
  subroutine conserve_diffuse_le_layout (gle)
×
3471
    use gs2_layouts, only: ik_idx, it_idx, ie_idx, il_idx, is_idx, ig_idx, le_lo
3472
    use le_grids, only: wxi, w, negrid
3473
    use run_parameters, only: ieqzip
3474
    use kt_grids, only: kwork_filter
3475
    implicit none
3476
    complex, dimension (:,:,le_lo%llim_proc:), intent (in out) :: gle
3477
    real :: delnu, vnE
3478
    integer :: ig, ik, ie, is, it, ile, ixi
3479
    complex :: v0y0, v1y1, v2y2
3480

3481
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3482
    ! First get v0y0 and then y1 = y0 - v0y0 * z0 / (1 + v0z0)
3483

3484
    !$OMP PARALLEL DO DEFAULT(none) &
3485
    !$OMP PRIVATE(ile, it, ik, is, ie, ig, ixi, vnE, v0y0) &
3486
    !$OMP SHARED(le_lo, kwork_filter, negrid, nxi_lim, w, wxi, ieqzip, bz0le, vnmult, vnew_E, aj0le, gle) &
3487
    !$OMP SCHEDULE(static)
3488
    do ile = le_lo%llim_proc, le_lo%ulim_proc
×
3489
       ik = ik_idx(le_lo, ile)
×
3490
       it = it_idx(le_lo, ile)
×
3491
       if (kwork_filter(it, ik)) cycle
×
3492
       if (ieqzip(it, ik)) cycle
×
3493
       is = is_idx(le_lo, ile)
×
3494
       ig = ig_idx(le_lo, ile)
×
3495
       v0y0 = 0.0
×
3496
       do ie = 1, negrid
×
3497
          vnE = vnmult(2) * vnew_E(ik, ie, is) * w(ie, is)
×
3498
          do ixi = 1, nxi_lim
×
3499
             v0y0 = v0y0 + vnE * aj0le(ixi, ie, ile) * gle(ixi, ie, ile) * wxi(ixi, ig)
×
3500
          end do
3501
       end do
3502
       gle(:, :, ile) = gle(:, :, ile) - bz0le(:, :, ile) * v0y0
×
3503
    end do
3504
    !$OMP END PARALLEL DO
3505

3506
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3507
    ! Now get v1y1 and then y2 = y1 - v1y1 * s1 / (1 + v1s1)
3508

3509
    !$OMP PARALLEL DO DEFAULT(none) &
3510
    !$OMP PRIVATE(ile, it, ik, is, ig, delnu, ie, ixi, v1y1) &
3511
    !$OMP SHARED(le_lo, kwork_filter, negrid, vnmult, delvnew, nxi_lim, &
3512
    !$OMP bs0le, vpa_aj0_le, gle, w, wxi, ieqzip) &
3513
    !$OMP SCHEDULE(static)
3514
    do ile = le_lo%llim_proc, le_lo%ulim_proc
×
3515
       ik = ik_idx(le_lo, ile)
×
3516
       it = it_idx(le_lo, ile)
×
3517
       if (kwork_filter(it, ik)) cycle
×
3518
       if (ieqzip(it, ik)) cycle
×
3519
       is = is_idx(le_lo, ile)
×
3520
       ig = ig_idx(le_lo, ile)
×
3521
       v1y1 = 0.0
×
3522
       do ie = 1, negrid
×
3523
          delnu = vnmult(2) * delvnew(ik, ie, is) * w(ie, is)
×
3524
          do ixi = 1, nxi_lim
×
3525
             v1y1 = v1y1 + vpa_aj0_le(ixi,  ie,  ile) * delnu * gle(ixi, ie, ile) * wxi(ixi, ig)
×
3526
          end do
3527
       end do
3528
       gle(:, :, ile) = gle(:, :, ile) - bs0le(:, :, ile) * v1y1
×
3529
    end do
3530
    !$OMP END PARALLEL DO
3531

3532
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3533
    ! Now get v2y2 and then get x = y2 - v2y2 * w2 / (1 + v2w2)
3534

3535
    !$OMP PARALLEL DO DEFAULT(none) &
3536
    !$OMP PRIVATE(ile, it, ik, is, ie, ig, ixi, delnu, v2y2) &
3537
    !$OMP SHARED(le_lo, kwork_filter, negrid, nxi_lim, ieqzip, &
3538
    !$OMP bw0le, delvnew, vperp_aj1le, gle, vnmult, w, wxi) &
3539
    !$OMP SCHEDULE(static)
3540
    do ile = le_lo%llim_proc, le_lo%ulim_proc
×
3541
       ik = ik_idx(le_lo, ile)
×
3542
       it = it_idx(le_lo, ile)
×
3543
       if (kwork_filter(it, ik)) cycle
×
3544
       if (ieqzip(it, ik)) cycle
×
3545
       is = is_idx(le_lo, ile)
×
3546
       ig = ig_idx(le_lo, ile)
×
3547
       v2y2 = 0.0
×
3548
       do ie = 1, negrid
×
3549
          delnu = vnmult(2) * delvnew(ik, ie, is) * w(ie, is)
×
3550
          do ixi = 1, nxi_lim
×
3551
             v2y2 = v2y2 + delnu * vperp_aj1le(ixi, ie, ile) * gle(ixi, ie, ile) * wxi(ixi, ig)
×
3552
          end do
3553
       end do
3554
       gle(:, :, ile) = gle(:, :, ile) - bw0le(:, :, ile) * v2y2
×
3555
    end do
3556
    !$OMP END PARALLEL DO
3557
  end subroutine conserve_diffuse_le_layout
×
3558

3559
  !> FIXME : Add documentation
3560
  subroutine solfp_lorentz_standard_layout (g, diagnostics)
×
3561
    use theta_grid, only: ntgrid
3562
    use le_grids, only: jend, lambda_map, il_is_wfb, ng2, integrate_moment, grid_has_trapped_particles
3563
    use gs2_layouts, only: ig_idx, ik_idx, il_idx, is_idx, it_idx, g_lo, lz_lo
3564
    use redistribute, only: gather, scatter
3565
    use run_parameters, only: ieqzip
3566
    use kt_grids, only: kwork_filter
3567
    use array_utils, only: zero_array
3568
    implicit none
3569
    complex, dimension (-ntgrid:,:,g_lo%llim_proc:), intent (in out) :: g
3570
    integer, optional, intent (in) :: diagnostics
3571
    complex, dimension (:,:), allocatable :: glz, glzc
×
3572
    complex, dimension (:,:,:), allocatable :: gc_or_gh
×
3573
    complex, dimension (nxi_lim) :: delta
×
3574
    complex :: fac, gwfb
3575
    integer :: ig, ik, il, is, it, je, nxi_scatt, ilz, cur_jend
3576
    logical :: is_wfb
3577

3578
    allocate (glz(nxi_lim,lz_lo%llim_proc:lz_lo%ulim_alloc))
×
3579
    call zero_array(glz)
×
3580

3581
    call gather (lambda_map, g, glz)
×
3582

3583
    if (heating .and. present(diagnostics)) then
×
3584
       allocate (glzc(nxi_lim, lz_lo%llim_proc:lz_lo%ulim_alloc))
×
3585
       call zero_array(glzc)
×
3586
       allocate (gc_or_gh(-ntgrid:ntgrid,2,g_lo%llim_proc:g_lo%ulim_alloc))
×
3587
       call zero_array(gc_or_gh)
×
3588

3589
       !$OMP PARALLEL DO DEFAULT(none) &
3590
       !$OMP PRIVATE(ilz, ig, je, il, fac) &
3591
       !$OMP SHARED(lz_lo, jend, ng2, glz, glzc, d1) &
3592
       !$OMP SCHEDULE(static)
3593
       do ilz = lz_lo%llim_proc, lz_lo%ulim_proc
×
3594
          ig = ig_idx(lz_lo,ilz)
×
3595

3596
          je = 2*jend(ig)
×
3597
          if (je == 0) then
×
3598
             je = 2 * ng2
×
3599
          end if
3600

3601
! when il=je-1 below, and we have trapped particles, glz is evaluated at glz(2*jend(ig),ilz).
3602
! this seems like a bug, since there are only 2*jend(ig)-1 grid points and
3603
! the value glz(2*jend(ig),ilz) corresponds to the value of g at xi = 0...this
3604
! doesn't make any sense...MAB
3605

3606
          do il = 1, je-1
×
3607
             fac = glz(il+1,ilz)-glz(il,ilz)
×
3608
             glzc(il,ilz) = conjg(fac)*fac*d1(il,ilz)  ! d1 accounts for hC(h) entropy
×
3609
          end do
3610
       end do
3611
       !$OMP END PARALLEL DO
3612

3613
       call scatter (lambda_map, glzc, gc_or_gh)
×
3614
       call integrate_moment (gc_or_gh, c_rate(:, :, :, :, 1))
×
3615

3616
       if (hyper_colls) then
×
3617
          !$OMP PARALLEL DO DEFAULT(none) &
3618
          !$OMP PRIVATE(ilz, ig, je, il, fac) &
3619
          !$OMP SHARED(lz_lo, jend, ng2, glz, glzc, h1) &
3620
          !$OMP SCHEDULE(static)
3621
          do ilz = lz_lo%llim_proc, lz_lo%ulim_proc
×
3622
             ig = ig_idx(lz_lo,ilz)
×
3623
             
3624
             je = 2*jend(ig)          
×
3625
             if (je == 0) then
×
3626
                je = 2*ng2 
×
3627
             end if
3628
             
3629
             do il = 1, je-1
×
3630
                fac = glz(il+1,ilz)-glz(il,ilz)
×
3631
                glzc(il,ilz) = conjg(fac)*fac*h1(il,ilz)  ! h1 accounts for hH(h) entropy
×
3632
             end do
3633
          end do
3634
          !$OMP END PARALLEL DO
3635

3636
          call scatter (lambda_map, glzc, gc_or_gh)
×
3637
          call integrate_moment(gc_or_gh, c_rate(:, :, :, :, 2))
×
3638
       end if
3639
       if (allocated(glzc)) deallocate (glzc)
×
3640
       if (allocated(gc_or_gh)) deallocate (gc_or_gh)
×
3641
    end if
3642

3643
    ! solve for glz row by row
3644
    !$OMP PARALLEL DO DEFAULT(none) &
3645
    !$OMP PRIVATE(ilz, ik, it, is, ig, je, nxi_scatt, &
3646
    !$OMP il, gwfb, delta, is_wfb, cur_jend) &
3647
    !$OMP SHARED(lz_lo, kwork_filter, ieqzip, vnew, force_collisions, jend, ng2, vpar_zero_mean, &
3648
    !$OMP glz, special_wfb_lorentz, ql, c1, betaa) &
3649
    !$OMP SCHEDULE(static)
3650
    do ilz = lz_lo%llim_proc, lz_lo%ulim_proc
×
3651
       is = is_idx(lz_lo,ilz)
×
3652
       ik = ik_idx(lz_lo,ilz)
×
3653
       if ( (abs(vnew(ik,1,is)) < 2.0*epsilon(0.0)) .and. .not. force_collisions) cycle
×
3654
       it = it_idx(lz_lo,ilz)
×
3655
       if(kwork_filter(it,ik))cycle
×
3656
       if (ieqzip(it,ik)) cycle
×
3657
       ig = ig_idx(lz_lo,ilz)
×
3658

3659
       !CMRDDGC, 10/2/2014:
3660
       ! Fixes for wfb treatment below, use same je definition in ALL cases
3661
       !   je  = #physical (unique) xi values + 1
3662
       !       NB +1 above WITH TRAPPED is duplicate xi=vpar=0 point with isign=2
3663
       !          +1 above WITHOUT TRAPPED is entirely unphysical extra grid point
3664
       !  je-1 = #physical xi values removing unphysical/duplicate extra point
3665
       cur_jend = jend(ig)
×
3666
       je = max(2 * cur_jend, 2 * ng2 + 1)
×
3667
       nxi_scatt = je - 1
×
3668
       is_wfb = il_is_wfb(cur_jend)
×
3669
       if (.not. grid_has_trapped_particles()) nxi_scatt = nxi_scatt - 1
×
3670

3671
       if (je == 2 * cur_jend .and. is_wfb .and. vpar_zero_mean) then
×
3672
          ! MRH if we have trapped particles and hence 2 vpar=0 points on the grid
3673
          ! use the average of both vpar = 0 points in the lorentz diffusion in pitch angle
3674
          ! this turns out to be necessary to suppress numerical instability
3675
          ! when we handle pitch angle scattering physically at theta = +/- pi
3676
          ! by setting special_wfb_lorentz =.false.
3677
          ! Note : This can give large change in g_wfb when it is not treated as a trapped
3678
          ! particle as the input g is unlikely to satisy g_wfb(sigma=1) ~ g_wfb(sigma=2).
3679
          ! Note : We don't apply this treatment to other trapped particles as we assume
3680
          ! g has a unique value at the bounce point for those particles.
3681
          glz(cur_jend, ilz) = (glz(cur_jend, ilz) + glz(2 * cur_jend, ilz)) / 2.0
×
3682
       end if
3683

3684
       ! zero unphysical/duplicate extra point. This shouldn't be needed
3685
       if (grid_has_trapped_particles()) glz(je, ilz) = 0.0
×
3686

3687
       if (is_wfb .and. special_wfb_lorentz) then
×
3688
          !CMRDDGC:  special_wfb_lorentz = t  => unphysical handling of wfb at bounce pt:
3689
          !          remove wfb from collisions, reinsert later
3690
          !
3691
          ! first save gwfb for reinsertion later
3692
          ! Note we don't save both sigma values as we force g(v||=0)_+ == g(v||=0)_-
3693
          ! at the end of the loop anyway.
3694
          gwfb = glz(ng2+1, ilz)
×
3695
          ! then remove vpa = 0 point, weight 0: (CMR confused by this comment!)
3696
          !The above is referring to the conservative scheme coefficients which involve
3697
          !1/pitch_weight but pitch_weight = 0 for the WFB at the WFB bounce point.
3698
          !Special_wfb_lorentz is a way to avoid this issue by ignoring the WFB pitch angle
3699
          glz(ng2+1:je-2, ilz) = glz(ng2+2:je-1, ilz)
×
3700
          ! Zero out the glz value not overwritten in the above. Shouldn't be needed
3701
          glz(je - 1, ilz) = 0.0
×
3702
          nxi_scatt = nxi_scatt - 1
×
3703
       end if
3704

3705
       ! right and left sweeps for tridiagonal solve:
3706
       delta(1) = glz(1, ilz)
×
3707
       do il = 1, nxi_scatt
×
3708
          delta(il+1) = glz(il+1, ilz) - ql(il+1, ilz) * delta(il)
×
3709
       end do
3710

3711
       glz(nxi_scatt+1, ilz) = delta(nxi_scatt+1) * betaa(nxi_scatt+1, ilz)
×
3712
       do il = nxi_scatt, 1, -1
×
3713
          glz(il, ilz) = (delta(il) - c1(il, ilz) * glz(il+1, ilz)) * betaa(il, ilz)
×
3714
       end do
3715

3716
       if (is_wfb .and. special_wfb_lorentz) then
×
3717
          glz(ng2+2:je-1, ilz) = glz(ng2+1:je-2, ilz)
×
3718
          glz(ng2+1, ilz) = gwfb
×
3719
       end if
3720

3721
       ! update the duplicate vpar=0 point with the post pitch angle scattering value
3722
       if (cur_jend /= 0) glz(2 * cur_jend, ilz) = glz(cur_jend, ilz)
×
3723
    end do
3724
    !$OMP END PARALLEL DO
3725

3726
    call scatter (lambda_map, glz, g)
×
3727
    deallocate (glz)
×
3728
  end subroutine solfp_lorentz_standard_layout
×
3729

3730
  !> FIXME : Add documentation
3731
  subroutine solfp_lorentz_le_layout (gle, diagnostics)
×
3732
    use le_grids, only: jend, ng2, negrid, integrate_moment, il_is_wfb, grid_has_trapped_particles
3733
    use gs2_layouts, only: ig_idx, ik_idx, is_idx, it_idx
3734
    use run_parameters, only: ieqzip
3735
    use gs2_layouts, only: le_lo
3736
    use kt_grids, only: kwork_filter
3737
    use array_utils, only: zero_array
3738
    implicit none
3739
    complex, dimension (:,:,le_lo%llim_proc:), intent (in out) :: gle
3740
    integer, optional, intent (in) :: diagnostics
3741
    complex, dimension (:), allocatable :: tmp
×
3742
    complex, dimension (:,:,:), allocatable :: glec
×
3743
    complex, dimension (nxi_lim) :: delta
×
3744
    complex :: fac, gwfb
3745
    integer :: ig, ik, il, is, je, it, ie, nxi_scatt, ile, cur_jend
3746
    logical :: is_wfb
3747

3748
    if (heating .and. present(diagnostics)) then
×
3749
       allocate (tmp(le_lo%llim_proc:le_lo%ulim_alloc)) ; call zero_array(tmp)
×
3750
       allocate (glec(nxi_lim, negrid+1, le_lo%llim_proc:le_lo%ulim_alloc))
×
3751
       call zero_array(glec)
×
3752
       !$OMP PARALLEL DO DEFAULT(none) &
3753
       !$OMP PRIVATE(ile, ig, je, ie, il, fac) &
3754
       !$OMP SHARED(le_lo, jend, ng2, negrid, gle, glec, d1le) &
3755
       !$OMP SCHEDULE(static)
3756
       do ile = le_lo%llim_proc, le_lo%ulim_proc
×
3757
          ig = ig_idx(le_lo,ile)
×
3758

3759
          je = 2*jend(ig)
×
3760
          if (je == 0) then
×
3761
             je = 2*ng2 
×
3762
          end if
3763

3764
! when il=je-1 below, and we have trapped particles, gle is evaluated at gle(2*jend(ig),ie,ile).
3765
! this seems like a bug, since there are only 2*jend(ig)-1 grid points and
3766
! the value gle(2*jend(ig),ie,ile) corresponds to the value of g at xi = 0...this
3767
! doesn't make any sense...MAB
3768

3769
          do ie = 1, negrid
×
3770
             do il = 1, je-1
×
3771
                fac = gle(il+1,ie,ile)-gle(il,ie,ile)
×
3772
                glec(il,ie,ile) = conjg(fac)*fac*d1le(il,ie,ile)  ! d1le accounts for hC(h) entropy
×
3773
             end do
3774
          end do
3775
       end do
3776
       !$OMP END PARALLEL DO
3777

3778
       call integrate_moment (le_lo, glec, tmp)
×
3779
       !$OMP PARALLEL DO DEFAULT(none) &
3780
       !$OMP PRIVATE(ile, ig, it, ik, is) &
3781
       !$OMP SHARED(le_lo, c_rate, tmp) &
3782
       !$OMP SCHEDULE(static)
3783
       do ile = le_lo%llim_proc, le_lo%ulim_proc
×
3784
          ig = ig_idx(le_lo,ile)
×
3785
          it = it_idx(le_lo,ile)
×
3786
          ik = ik_idx(le_lo,ile)
×
3787
          is = is_idx(le_lo,ile)
×
3788
          c_rate(ig,it,ik,is,1) = tmp(ile)
×
3789
       end do
3790
       !$OMP END PARALLEL DO
3791

3792
       if (hyper_colls) then
×
3793
          !$OMP PARALLEL DO DEFAULT(none) &
3794
          !$OMP PRIVATE(ile, ig, je, ie, il, fac) &
3795
          !$OMP SHARED(le_lo, jend, ng2, negrid, gle, glec, h1le) &
3796
          !$OMP SCHEDULE(static)
3797
          do ile = le_lo%llim_proc, le_lo%ulim_proc
×
3798
             ig = ig_idx(le_lo,ile)
×
3799

3800
             je = 2*jend(ig)
×
3801
             if (je == 0) then
×
3802
                je = 2*ng2
×
3803
             end if
3804

3805
             do ie = 1, negrid
×
3806
                do il = 1, je-1
×
3807
                   fac = gle(il+1,ie,ile)-gle(il,ie,ile)
×
3808
                   glec(il,ie,ile) = conjg(fac)*fac*h1le(il,ie,ile)  ! h1le accounts for hH(h) entropy
×
3809
                end do
3810
             end do
3811
          end do
3812
          !$OMP END PARALLEL DO
3813

3814
          call integrate_moment (le_lo, glec, tmp)
×
3815

3816
          !$OMP PARALLEL DO DEFAULT(none) &
3817
          !$OMP PRIVATE(ile, ig, it, ik, is) &
3818
          !$OMP SHARED(le_lo, c_rate, tmp) &
3819
          !$OMP SCHEDULE(static)
3820
          do ile = le_lo%llim_proc, le_lo%ulim_proc
×
3821
             ig = ig_idx(le_lo,ile)
×
3822
             it = it_idx(le_lo,ile)
×
3823
             ik = ik_idx(le_lo,ile)
×
3824
             is = is_idx(le_lo,ile)
×
3825
             c_rate(ig,it,ik,is,2) = tmp(ile)
×
3826
          end do
3827
          !$OMP END PARALLEL DO
3828
       end if
3829
       deallocate(tmp, glec)
×
3830
    end if
3831

3832
    ! solve for gle row by row
3833
    !$OMP PARALLEL DO DEFAULT(none) &
3834
    !$OMP PRIVATE(ile, is, it, ik, ig, il, ie, je, nxi_scatt, &
3835
    !$OMP gwfb, delta, is_wfb, cur_jend) &
3836
    !$OMP SHARED(le_lo, kwork_filter, ieqzip, vnew, force_collisions, jend, ng2, special_wfb_lorentz, &
3837
    !$OMP vpar_zero_mean, negrid, gle, qle, betaale, c1le) &
3838
    !$OMP SCHEDULE(static)
3839
    do ile = le_lo%llim_proc, le_lo%ulim_proc
×
3840
       is = is_idx(le_lo, ile)
×
3841
       ik = ik_idx(le_lo, ile)
×
3842
       if ((abs(vnew(ik, 1, is)) < 2.0 * epsilon(0.0)) .and. .not. force_collisions) cycle
×
3843
       it = it_idx(le_lo, ile)
×
3844
       if (kwork_filter(it, ik)) cycle
×
3845
       if (ieqzip(it, ik)) cycle
×
3846
       ig = ig_idx(le_lo, ile)
×
3847

3848
       !CMRDDGC, 10/2/1014:
3849
       ! Fixes for wfb treatment below, use same je definition in ALL cases
3850
       !   je  = #physical xi values at location, includes duplicate point at vpar=0
3851
       !  je-1 = #physical xi values removing duplicate vpar=0 point
3852
       cur_jend = jend(ig)
×
3853
       je = max(2 * cur_jend, 2 * ng2 + 1)
×
3854
       nxi_scatt = je - 1
×
3855
       is_wfb = il_is_wfb(cur_jend)
×
3856
       if ((is_wfb .and. special_wfb_lorentz) .or. .not. grid_has_trapped_particles()) &
×
3857
            nxi_scatt = nxi_scatt - 1
×
3858

3859
       do ie = 1, negrid
×
3860
          if (je == 2 * cur_jend .and. is_wfb .and. vpar_zero_mean) then
×
3861
             ! MRH if we have trapped particles and hence 2 vpar=0 points on the grid
3862
             ! use the average of both vpar = 0 points in the lorentz diffusion in pitch angle
3863
             ! this turns out to be necessary to suppress numerical instability
3864
             ! when we handle pitch angle scattering physically at theta = +/- pi
3865
             ! by setting special_wfb_lorentz =.false.
3866
             ! Note : This can give large change in g_wfb when it is not treated as a trapped
3867
             ! particle as the input g is unlikely to satisy g_wfb(sigma=1) ~ g_wfb(sigma=2).
3868
             ! Note : We don't apply this treatment to other trapped particles as we assume
3869
             ! g has a unique value at the bounce point for those particles.
3870
             gle(cur_jend, ie, ile) = (gle(cur_jend, ie, ile) + &
×
3871
                  gle(2 * cur_jend, ie, ile)) / 2.0
×
3872
          end if
3873

3874
          ! zero redundant duplicate xi, isign=2 for vpar=0! This shouldn't be needed
3875
          if (grid_has_trapped_particles()) gle(je, ie, ile) = 0.0d0
×
3876

3877
          if (is_wfb .and. special_wfb_lorentz) then
×
3878
             !CMRDDGC:  special_wfb_lorentz = t  => unphysical handling of wfb at bounce pt:
3879
             !          remove wfb from collisions, reinsert later
3880
             !
3881
             ! first save gwfb for reinsertion later
3882
             ! Note we don't save both sigma values as we force g(v||=0)_+ == g(v||=0)_-
3883
             ! at the end of the loop anyway.
3884
             gwfb = gle(ng2 + 1, ie, ile)
×
3885
             ! then remove vpa = 0 point, weight 0: (CMR confused by this comment!)
3886
             !The above is referring to the conservative scheme coefficients which involve
3887
             !1/pitch_weight but pitch_weight = 0 for the WFB at the WFB bounce point.
3888
             !Special_wfb_lorentz is a way to avoid this issue by ignoring the WFB pitch angle
3889
             gle(ng2+1:je-2, ie, ile) = gle(ng2+2:je-1, ie, ile)
×
3890
             ! Zero out the gle value not overwritten in the above. Shouldn't be needed
3891
             gle(je - 1, ie, ile) = 0.0
×
3892
          end if
3893

3894
          ! right and left sweeps for tridiagonal solve:
3895
          delta(1) = gle(1, ie, ile)
×
3896
          do il = 1, nxi_scatt
×
3897
             delta(il+1) = gle(il+1, ie, ile) - qle(il+1, ie, ile) * delta(il)
×
3898
          end do
3899

3900
          gle(nxi_scatt+1, ie, ile) = delta(nxi_scatt+1) * betaale(nxi_scatt+1, ie, ile)
×
3901
          do il = nxi_scatt, 1, -1
×
3902
             gle(il, ie, ile) = (delta(il) - c1le(il, ie, ile) * gle(il+1, ie, ile)) * &
×
3903
                  betaale(il, ie, ile)
×
3904
          end do
3905

3906
          if (is_wfb .and. special_wfb_lorentz) then
×
3907
             gle(ng2+2:je-1, ie, ile) = gle(ng2+1:je-2, ie, ile)
×
3908
             gle(ng2+1, ie, ile) = gwfb
×
3909
          end if
3910

3911
          ! next line ensures bounce condition is satisfied after lorentz collisions
3912
          ! this is right thing to do, but it would mask any prior bug in trapping condition!
3913
          if (cur_jend /= 0) gle(2 * cur_jend, ie, ile) = gle(cur_jend, ie, ile)
×
3914

3915
       end do
3916
    end do
3917
    !$OMP END PARALLEL DO
3918
  end subroutine solfp_lorentz_le_layout
×
3919

3920
  !> Energy diffusion subroutine used with energy layout (not le_layout)
3921
  !> this is always the case when initializing the conserving terms,
3922
  !> otherwise is the case if use_le_layout is no specified in the input file.
3923
  subroutine solfp_ediffuse_standard_layout (g)
×
3924
    use theta_grid, only: ntgrid
3925
    use le_grids, only: negrid, forbid, energy_map
3926
    use gs2_layouts, only: ig_idx, it_idx, ik_idx, il_idx, is_idx, e_lo, g_lo
3927
    use redistribute, only: gather, scatter
3928
    use run_parameters, only: ieqzip
3929
    use kt_grids, only: kwork_filter
3930
    use array_utils, only: zero_array
3931
    implicit none
3932
    complex, dimension (-ntgrid:,:,g_lo%llim_proc:), intent (in out) :: g
3933
    integer :: ie, is, ig, il, it, ik, ielo
3934
    complex, dimension (negrid) :: delta
×
3935
    complex, dimension (:,:), allocatable :: ged
×
3936

3937
    allocate (ged(negrid+1,e_lo%llim_proc:e_lo%ulim_alloc)) ; call zero_array(ged)
×
3938
    call gather (energy_map, g, ged)
×
3939

3940
    ! solve for ged row by row
3941
    !$OMP PARALLEL DO DEFAULT(none) &
3942
    !$OMP PRIVATE(ielo, it, ik, is, ig, il, delta, ie) &
3943
    !$OMP SHARED(e_lo, kwork_filter, ieqzip, vnew, force_collisions, forbid, negrid, &
3944
    !$OMP ged, eql, ec1, ebetaa) &
3945
    !$OMP SCHEDULE(static)
3946
    do ielo = e_lo%llim_proc, e_lo%ulim_proc
×
3947
       is = is_idx(e_lo, ielo)
×
3948
       ik = ik_idx(e_lo, ielo)
×
3949
       if ((abs(vnew(ik, 1, is)) < 2.0 * epsilon(0.0)) .and. .not. force_collisions) cycle
×
3950
       it = it_idx(e_lo, ielo)
×
3951
       if (kwork_filter(it, ik))cycle
×
3952
       if (ieqzip(it, ik)) cycle
×
3953
       ig = ig_idx(e_lo, ielo)
×
3954
       il = il_idx(e_lo, ielo)
×
3955
       if (forbid(ig, il)) cycle
×
3956

3957
       delta(1) = ged(1, ielo)
×
3958
       do ie = 1, negrid-1
×
3959
          delta(ie+1) = ged(ie+1, ielo) - eql(ie+1, ielo) * delta(ie)
×
3960
       end do
3961
       
3962
       ged(negrid+1, ielo) = 0.0
×
3963
       do ie = negrid, 1, -1
×
3964
          ged(ie, ielo) = (delta(ie) - ec1(ie, ielo) * ged(ie+1, ielo)) * ebetaa(ie, ielo)
×
3965
       end do
3966
    end do
3967
    !$OMP END PARALLEL DO
3968

3969
    call scatter (energy_map, ged, g)
×
3970
    deallocate (ged)
×
3971
  end subroutine solfp_ediffuse_standard_layout
×
3972

3973
  !> FIXME : Add documentation
3974
  subroutine solfp_ediffuse_le_layout (gle)
×
3975
    use le_grids, only: negrid, jend, ng2
3976
    use gs2_layouts, only: ig_idx, it_idx, ik_idx, is_idx, le_lo
3977
    use run_parameters, only: ieqzip
3978
    use kt_grids, only: kwork_filter
3979
    implicit none
3980
    complex, dimension (:,:,le_lo%llim_proc:), intent (in out) :: gle
3981
    integer :: ie, is, ig, ile, ixi, ik, it, max_xi
3982
    complex, dimension (negrid) :: delta
×
3983
    
3984
    ! solve for gle row by row
3985
    !$OMP PARALLEL DO DEFAULT(none) &
3986
    !$OMP PRIVATE(ile, is, it, ik, ig, ixi, max_xi, ie, delta) &
3987
    !$OMP SHARED(le_lo, vnew, force_collisions, kwork_filter, ieqzip, jend, ng2, &
3988
    !$OMP gle, negrid, eqle, ec1le, ebetaale) &
3989
    !$OMP SCHEDULE(static)
3990
    do ile = le_lo%llim_proc, le_lo%ulim_proc
×
3991
       is = is_idx(le_lo, ile)
×
3992
       ik = ik_idx(le_lo, ile)
×
3993
       if ((abs(vnew(ik, 1, is)) < 2.0 * epsilon(0.0)) .and. .not. force_collisions) cycle
×
3994
       it = it_idx(le_lo, ile)
×
3995
       if (kwork_filter(it, ik)) cycle
×
3996
       if (ieqzip(it, ik)) cycle
×
3997
       ig = ig_idx(le_lo, ile)
×
3998
       max_xi = max(2 * jend(ig), 2 * ng2)
×
3999
       do ixi = 1, max_xi
×
4000
          delta(1) = gle(ixi, 1, ile)
×
4001
          do ie = 1, negrid - 1
×
4002
             delta(ie+1) = gle(ixi, ie+1, ile) - eqle(ixi, ie+1, ile) * delta(ie)
×
4003
          end do
4004

4005
          gle(ixi, negrid+1, ile) = 0.0
×
4006
          do ie = negrid, 1, -1
×
4007
             gle(ixi, ie, ile) = (delta(ie) - ec1le(ixi, ie, ile) * gle(ixi, ie+1, ile)) &
×
4008
                  * ebetaale(ixi, ie, ile)
×
4009
          end do
4010
       end do
4011
    end do
4012
    !$OMP END PARALLEL DO
4013
  end subroutine solfp_ediffuse_le_layout
×
4014

4015
  !> FIXME : Add documentation
4016
  subroutine init_vpdiff
×
4017
    use le_grids, only: al, nlambda, jend, ng2, il_is_wfb, il_is_passing, ixi_to_il, ixi_to_isgn
4018
    use theta_grid, only: ntgrid, bmag
4019
    use array_utils, only: zero_array
4020
    implicit none
4021

4022
    integer :: il, isgn, ixi, ig, je, te
4023
    real :: slb0, slb1, slb2, slbl, slbr
4024

4025
    if (.not. allocated(vpdiff) .and. conservative) then
×
4026

4027
       allocate (vpdiff(-ntgrid:ntgrid, 2, nlambda)) ; call zero_array(vpdiff)
×
4028
       
4029
       do ig = -ntgrid, ntgrid
×
4030
          je = jend(ig)
×
4031
          if (il_is_passing(je) .or. (il_is_wfb(je) .and. special_wfb_lorentz)) then
×
4032
             te = ng2
×
4033
          else
4034
             te = je
×
4035
          end if
4036
          do il = 2, te-1
×
4037
             slb0 = safe_sqrt(1.0 - bmag(ig) * al(il-1))
×
4038
             slb1 = safe_sqrt(1.0 - bmag(ig) * al(il))
×
4039
             slb2 = safe_sqrt(1.0 - bmag(ig) * al(il+1))
×
4040
             
4041
             slbl = (slb1 + slb0) * 0.5  ! xi(j-1/2)
×
4042
             slbr = (slb1 + slb2) * 0.5  ! xi(j+1/2)
×
4043
             
4044
             vpdiff(ig, 1, il) = (slbl**2 - slbr**2) / pitch_weights(il, ig)
×
4045
          end do
4046

4047
          ! boundary at xi = 1
4048
          slb1 = safe_sqrt(1.0 - bmag(ig) * al(1))
×
4049
          slb2 = safe_sqrt(1.0 - bmag(ig) * al(2))
×
4050
          slbr = 0.5 * (slb1 + slb2)
×
4051
          vpdiff(ig, 1, 1) = (1.0 - slbr**2) / pitch_weights(1, ig)
×
4052
          
4053
          ! boundary at xi = 0
4054
          il = te
×
4055
          slb0 = safe_sqrt(1.0 - bmag(ig) * al(il-1))
×
4056
          if (te == ng2) then ! Passing
×
4057
             slb1 = safe_sqrt(1.0 - bmag(ig) * al(il))
×
4058
             slb2 = -slb1
×
4059
          else
4060
             ! We would expect safe_sqrt(1.0 - bmag(ig) * al(il)) = 0 here so could just
4061
             ! use slb1 = safe_sqrt(1.0 - bmag(ig) * al(il)) = 0 for both branches?
4062
             slb1 = 0.0
×
4063
             slb2 = -slb0
×
4064
          end if
4065
          
4066
          slbl = (slb1 + slb0) * 0.5
×
4067
          slbr = (slb1 + slb2) * 0.5
×
4068

4069
          if (abs(pitch_weights(il, ig)) <= epsilon(0.0)) then
×
4070
             vpdiff(ig, 1, il) = 0.0
×
4071
          else
4072
             vpdiff(ig, 1, il) = (slbl**2 - slbr**2) / pitch_weights(il, ig)
×
4073
          end if
4074
          
4075
          vpdiff(ig, 2, :) = -vpdiff(ig, 1, :)
×
4076
          
4077
       end do
4078

4079
       if (use_le_layout) then
×
4080
          allocate(vpdiffle(nxi_lim, -ntgrid:ntgrid))
×
4081

4082
          do ig = -ntgrid, ntgrid
×
4083
             do ixi = 1, nxi_lim
×
4084
                il = ixi_to_il(ixi, ig)
×
4085
                isgn = ixi_to_isgn(ixi, ig)
×
4086
                vpdiffle(ixi, ig) = vpdiff(ig, isgn, il)
×
4087
             end do
4088
          end do
4089
       end if
4090
    end if
4091
    
4092
  end subroutine init_vpdiff
×
4093

4094
  !> Forces recalculation of coefficients in collision operator
4095
  !! when timestep changes.
4096
  subroutine finish_collisions
×
4097
    implicit none
4098

4099
    ! This is a bit of a hack to make sure that we get the correct vnmult
4100
    ! value during a timestep change. Whilst we do restore vnmult from the
4101
    ! restart file, this unfortunately doesn't happen until after we've initialised
4102
    ! the collision operator so we choose to store this in vnm_init, which is
4103
    ! where we get our initial value of vnmult from anyway.
4104
    vnm_init = vnmult
×
4105
    vnmult = -1.0
×
4106
    initialized = .false.  
×
4107

4108
    if (allocated(c_rate)) deallocate (c_rate)
×
4109
    if (allocated(z0)) deallocate (z0, w0, s0)
×
4110
    if (allocated(bz0)) deallocate (bz0, bw0, bs0)
×
4111
    if (allocated(vnew)) deallocate (vnew, vnew_s, vnew_D, vnew_E, delvnew)
×
4112
    if (allocated(vnewh)) deallocate (vnewh)
×
4113
    if (allocated(pitch_weights)) deallocate(pitch_weights)
×
4114

4115
    if(use_le_layout) then
×
4116
      if (allocated(c1le)) then
×
4117
         deallocate (c1le, betaale, qle)
×
4118
         if (heating) deallocate (d1le, h1le)
×
4119
      end if
4120
      if (allocated(ec1le)) deallocate (ec1le, ebetaale, eqle)
×
4121
      if (allocated(s0le)) deallocate(s0le)
×
4122
      if (allocated(w0le)) deallocate(w0le)
×
4123
      if (allocated(z0le)) deallocate(z0le)
×
4124
      if (allocated(aj0le)) deallocate(aj0le)
×
4125
      if (allocated(vperp_aj1le)) deallocate(vperp_aj1le)
×
4126
      if (allocated(vpa_aj0_le)) deallocate(vpa_aj0_le)
×
4127
      if (allocated(bs0le)) deallocate(bs0le)
×
4128
      if (allocated(bw0le)) deallocate(bw0le)
×
4129
      if (allocated(bz0le)) deallocate(bz0le)
×
4130
    else
4131
      if (allocated(c1)) then
×
4132
         deallocate (c1, betaa, ql)
×
4133
         if (heating) deallocate (d1, h1)
×
4134
      end if
4135
      if (allocated(ec1)) deallocate (ec1, ebetaa, eql)
×
4136
    end if
4137
    if (allocated(vpdiff)) deallocate (vpdiff)
×
4138
    if (allocated(vpdiffle)) deallocate (vpdiffle)
×
4139
    if (allocated(dtot)) deallocate (dtot, fdf, fdb)
×
4140
  end subroutine finish_collisions
×
4141

4142
#include "collisions_auto_gen.inc"  
4143
end module collisions
×
4144

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