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

gyrokinetics / gs2 / 1970319704

06 Aug 2025 03:50PM UTC coverage: 8.18% (+0.002%) from 8.178%
1970319704

push

gitlab-ci

David Dickinson
Merged in bugfix/fix_issue_88_multiple_ncheck (pull request #1109)

3667 of 44831 relevant lines covered (8.18%)

124503.35 hits per line

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

0.0
/src/gs2_diagnostics.fpp
1
!> A module for calculating and writing the main GS2 outputs.
2
module gs2_diagnostics
3
  use gs2_heating, only: heating_diagnostics
4
  use gs2_save, only: save_many
5
  use diagnostics_configuration, only: diagnostics_config_type
6
  use gs2_io, only: get_netcdf_file_id
7

8
  ! what about boundary condition contributions?  In the presence 
9
  ! of magnetic shear, there are not always zero amplitudes at the ends
10
  ! of the supercells.
11

12
  implicit none
13

14
  private
15

16
  public :: read_parameters, init_gs2_diagnostics, finish_gs2_diagnostics
17
  public :: check_gs2_diagnostics, wnml_gs2_diagnostics, check_restart_file_writeable
18
  public :: loop_diagnostics, omegaavg, calculate_instantaneous_omega
19
  public :: diffusivity, calculate_simple_quasilinear_flux_metric_by_k
20
  public :: save_restart_dist_fn, do_write_fyx, do_write_f, do_write_parity
21
  public :: do_write_heating, do_write_nl_flux_dist, do_write_geom, heating
22
  public :: save_distfn, save_glo_info_and_grids, save_velocities, save_for_restart
23
  public :: omegatol, nwrite, nmovie, navg, nsave, make_movie, exit_when_converged
24
  public :: do_write_correlation_extend, do_write_correlation, do_write_symmetry
25

26
  real :: omegatinst, omegatol, damped_threshold, omega_checks_start
27
  logical :: print_line, print_flux_line, print_summary, write_line, write_flux_line
28
  logical :: write_omega, write_omavg, write_ascii, write_gs, write_ql_metric
29
  logical :: write_g, write_gyx, write_eigenfunc, write_fields, write_final_fields
30
  logical :: write_final_antot, write_final_moments, write_avg_moments, write_parity
31
  logical :: write_moments, ob_midplane, write_final_db, write_full_moments_notgc
32
  logical :: write_cross_phase, write_final_epar, write_kpar, write_heating, write_lorentzian
33
  logical :: write_fluxes, write_fluxes_by_mode, write_kinetic_energy_transfer, write_verr
34
  logical :: write_cerr, write_max_verr, exit_when_converged, use_nonlin_convergence
35
  logical :: save_for_restart, save_distfn, save_glo_info_and_grids, save_velocities
36
  logical :: write_symmetry, write_correlation_extend, write_correlation, make_movie
37
  logical :: write_nl_flux_dist, file_safety_check, append_old
38
  logical :: write_phi_over_time, write_apar_over_time, write_bpar_over_time, write_jext
39
  integer :: nwrite, nmovie, navg, nsave, igomega, nwrite_mult, nc_sync_freq
40

41
  ! internal. `write_any` differs from new diagnostics input `write_any`. The
42
  ! latter can be used to turn off all output, whereas here, it's used to
43
  ! _check_ if any outputs are enabled
44
  logical :: write_any, write_any_fluxes, dump_any
45
  logical, private :: initialized = .false.
46
  complex, allocatable, dimension (:,:,:) :: domega
47

48
  !> Complex frequency, and time-averaged complex frequency
49
  complex, dimension (:, :), allocatable :: omega, omegaavg
50

51
  integer :: out_unit, heat_unit, heat_unit2, lpc_unit
52
  integer :: jext_unit, phase_unit, dist_unit, yxdist_unit
53
  integer :: res_unit, res_unit2, parity_unit, cres_unit
54
  integer :: conv_nstep_av, conv_min_step, conv_max_step
55
  integer :: conv_nsteps_converged
56

57
  !> Frequency time history, size `(navg, ntheta0, naky)`
58
  complex, dimension (:,:,:), allocatable :: omegahist
59

60
  !> Heating diagnostics summed over space at the current timestep
61
  type (heating_diagnostics) :: h
62
  !> Heating diagnostics summed over space over the last [[gs2_diagnostics_knobs:navg]] timesteps
63
  type (heating_diagnostics), dimension(:), save, allocatable :: h_hist
64
  !> Heating diagnostics as a function of \((\theta, k_y)\) at the current timestep
65
  type (heating_diagnostics), dimension(:,:), save, allocatable :: hk
66
  !> Heating diagnostics as a function of \((\theta, k_y)\) over the last [[gs2_diagnostics_knobs:navg]] timesteps
67
  type (heating_diagnostics), dimension(:,:,:), save, allocatable :: hk_hist
68
  !GGH J_external
69
  real, dimension(:,:,:), allocatable ::  j_ext_hist
70

71
  real, dimension (:,:,:), allocatable ::  pflux_tormom
72

73
  real, dimension (:), allocatable :: pflux_avg, qflux_avg, heat_avg, vflux_avg
74
  real :: conv_test_multiplier
75

76
  integer :: nout = 1
77
  integer :: nout_movie = 1
78
  integer :: nout_big = 1
79
  complex :: wtmp_old = 0.
80

81
contains
82

83
  !> Write the diagnostics namelist to file
84
  subroutine wnml_gs2_diagnostics(unit)
×
85
    use diagnostics_configuration, only: diagnostics_config
86
    implicit none
87
    !> Unit of an open file to write to
88
    integer, intent(in) :: unit
89
    call diagnostics_config%write(unit)
×
90
  end subroutine wnml_gs2_diagnostics
×
91

92
  !> Perform some basic checking of the diagnostic input parameters, and write the results to file
93
  subroutine check_gs2_diagnostics(report_unit)
×
94
    use file_utils, only: run_name
95
    use nonlinear_terms, only: nonlin
96
    use dist_fn, only : def_parity, even 
97
    use kt_grids, only : is_box
98
    use init_g, only : restart_file
99
    use gs2_save, only: restart_writable
100
    implicit none
101
    !> Unit of an open file to write to
102
    integer, intent(in) :: report_unit
103
    logical :: writable
104
    write (report_unit, *) 
×
105
    write (report_unit, fmt="('------------------------------------------------------------')")
×
106
    write (report_unit, *) 
×
107
    write (report_unit, fmt="('Diagnostic control section.')")
×
108

109
    if (print_line) then
×
110
       write (report_unit, fmt="('print_line = T:            Estimated frequencies &
111
            & output to the screen every ',i4,' steps.')") nwrite
×
112
    else
113
       ! nothing
114
    end if
115

116
    if (write_line) then
×
117
       if (write_ascii) then
×
118
          write (report_unit, fmt="('write_line = T:            Estimated frequencies output to ',a,' every ',i4,' steps.')") &
119
               & trim(run_name)//'.out',  nwrite
×
120
       end if
121
       write (report_unit, fmt="('write_line = T:            Estimated frequencies output to ',a,' every ',i4,' steps.')") &
122
            & trim(run_name)//'.out.nc',  nwrite
×
123
    else
124
       ! nothing
125
    end if
126

127
    if (print_flux_line) then
×
128
       write (report_unit, fmt="('print_flux_line = T:       Instantaneous fluxes output to screen every ', &
129
            & i4,' steps.')") nwrite
×
130
    else
131
       ! nothing
132
    end if
133

134
    if (write_flux_line) then
×
135
       if (write_ascii) then
×
136
          write (report_unit, fmt="('write_flux_line = T:       Instantaneous fluxes output to ',a,' every ',i4,' steps.')") &
137
               & trim(run_name)//'.out',  nwrite
×
138
       end if
139
       write (report_unit, fmt="('write_flux_line = T:       Instantaneous fluxes output to ',a,' every ',i4,' steps.')") &
140
            & trim(run_name)//'.out.nc',  nwrite
×
141
    else
142
       ! nothing
143
    end if
144

145
    if (write_omega) then
×
146
       if (write_ascii) then
×
147
          write (report_unit, fmt="('write_omega = T:           Instantaneous frequencies written to ',a)") trim(run_name)//'.out'
×
148
       else
149
          write (report_unit, fmt="('write_omega = T:           No effect.')")
×
150
       end if
151
       write (report_unit, fmt="('                           Frequencies calculated at igomega = ',i4)") igomega
×
152
       if (def_parity .and. .not. even) then
×
153
          write (report_unit, fmt="('################# WARNING #######################')")
×
154
          write (report_unit, fmt="('   You probably want igomega /= 0 for odd parity modes.')") 
×
155
          write (report_unit, fmt="('################# WARNING #######################')")
×
156
          write (report_unit, *) 
×
157
       end if
158
    end if
159

160
    if (write_omavg) then
×
161
       if (write_ascii) then
×
162
          write (report_unit, fmt="('write_omavg = T:           Time-averaged frequencies written to ',a)") trim(run_name)//'.out'
×
163
          write (report_unit, fmt="('                           Averages taken over ',i4,' timesteps.')") navg
×
164
       else
165
          write (report_unit, fmt="('write_omavg = T:           No effect.')")
×
166
       end if
167
    end if
168

169
    if (write_ascii) then
×
170
       write (report_unit, fmt="('write_ascii = T:           Write some data to ',a)") trim(run_name)//'.out'
×
171
    end if
172

173
    if (write_eigenfunc) then
×
174
       if (write_ascii) then
×
175
          write (report_unit, fmt="('write_eigenfunc = T:       Normalized Phi(theta) written to ',a)") trim(run_name)//'.eigenfunc'
×
176
       end if
177
       write (report_unit, fmt="('write_eigenfunc = T:       Normalized Phi(theta) written to ',a)") trim(run_name)//'.out.nc'
×
178
    end if
179

180
    if (write_final_fields) then
×
181
       if (write_ascii) then
×
182
          write (report_unit, fmt="('write_final_fields = T:    Phi(theta), etc. written to ',a)") trim(run_name)//'.fields'
×
183
       end if
184
       write (report_unit, fmt="('write_final_fields = T:    Phi(theta), etc. written to ',a)") trim(run_name)//'.out.nc'
×
185
    end if
186

187
    if (write_final_antot) then
×
188
       if (write_ascii) then
×
189
          write (report_unit, fmt="('write_final_antot = T:          Sources for Maxwell eqns. written to ',a)") &
190
               & trim(run_name)//'.antot'
×
191
       end if
192
       write (report_unit, fmt="('write_final_antot = T:          Sources for Maxwell eqns. written to ',a)") &
193
            & trim(run_name)//'.out.nc'
×
194
    end if
195

196
    if (write_final_moments) then
×
197
       if (write_ascii) then
×
198
          write (report_unit, fmt="('write_final_moments = T:   Low-order moments of g written to ',a)") &
199
               & trim(run_name)//'.moments'
×
200
          write (report_unit, fmt="('write_final_moments = T:   int dl/B average of low-order moments of g written to ',a)") &
201
               & trim(run_name)//'.amoments'
×
202
       end if
203
       write (report_unit, fmt="('write_final_moments = T:   Low-order moments of g written to ',a)") &
204
            & trim(run_name)//'.out.nc'
×
205
       write (report_unit, fmt="('write_final_moments = T:   int dl/B average of low-order moments of g written to ',a)") &
206
            & trim(run_name)//'.out.nc'
×
207
    end if
208

209
    if (write_avg_moments) then
×
210
       if (.not. is_box) then
×
211
          write (report_unit, *) 
×
212
          write (report_unit, fmt="('################# WARNING #######################')")
×
213
          write (report_unit, fmt="('write_avg_moments = T:          Ignored unless grid_option=box')")
×
214
          write (report_unit, fmt="('################# WARNING #######################')")
×
215
          write (report_unit, *) 
×
216
       else
217
          if (write_ascii) then
×
218
             write (report_unit, fmt="('write_avg_moments = T:     Flux surface averaged low-order moments of g written to ',a)") &
219
                  & trim(run_name)//'.moments'
×
220
          end if
221
          write (report_unit, fmt="('write_avg_moments = T:     Flux surface averaged low-order moments of g written to ',a)") &
222
               & trim(run_name)//'.out.nc'
×
223
       end if
224
    end if
225

226
    if (write_final_epar) then
×
227
       if (write_ascii) then
×
228
          write (report_unit, fmt="('write_final_epar = T:      E_parallel(theta) written to ',a)") trim(run_name)//'.epar'
×
229
       end if
230
       write (report_unit, fmt="('write_final_epar = T:      E_parallel(theta) written to ',a)") trim(run_name)//'.out.nc'
×
231
    end if
232

233
    if (write_fluxes) then
×
234
       if (write_ascii) then
×
235
          write (report_unit, fmt="('write_fluxes = T:         Phi**2(kx, ky) written to ',a)") trim(run_name)//'.out'
×
236
       end if
237
    else
238
       write (report_unit, fmt="('write_fluxes = F:         Phi**2(kx, ky) NOT written to ',a)") trim(run_name)//'.out'
×
239
    end if
240

241
    if (save_for_restart) then
×
242
       write (report_unit, fmt="('save_for_restart = T:      Restart files written to ',a)") trim(restart_file)//'.(PE)'
×
243
    else
244
       if (nonlin) then
×
245
          write (report_unit, *) 
×
246
          write (report_unit, fmt="('################# WARNING #######################')")
×
247
          write (report_unit, fmt="('save_for_restart = F:              This run cannot be continued.')")
×
248
          write (report_unit, fmt="('THIS IS PROBABLY AN ERROR.')") 
×
249
          write (report_unit, fmt="('################# WARNING #######################')")
×
250
          write (report_unit, *) 
×
251
       end if
252
    end if
253

254
    !Verify restart file can be written
255
    if((save_for_restart.or.save_distfn).and.(file_safety_check))then
×
256
       !Can we write file?
257
       writable=restart_writable()
×
258

259
       !If we can't write the restart file then we should probably quit
260
       if((.not.writable))then
×
261
          if(save_for_restart)then 
×
262
             write (report_unit, *) 
×
263
             write (report_unit, fmt="('################# WARNING #######################')")
×
264
             write (report_unit, fmt="('save_for_restart = T:   But we cannot write to a test file like ',A,'.')") trim(restart_file)
×
265
             write (report_unit, fmt="('THIS IS PROBABLY AN ERROR --> Check restart_dir.')") 
×
266
             write (report_unit, fmt="('################# WARNING #######################')")
×
267
             write (report_unit, *) 
×
268
          endif
269
          if(save_distfn)then 
×
270
             write (report_unit, *) 
×
271
             write (report_unit, fmt="('################# WARNING #######################')")
×
272
             write (report_unit, fmt="('save_distfn = T:   But we cannot write to a test file like ',A,'.')") trim(restart_file)
×
273
             write (report_unit, fmt="('THIS IS PROBABLY AN ERROR --> Check restart_dir.')") 
×
274
             write (report_unit, fmt="('################# WARNING #######################')")
×
275
             write (report_unit, *) 
×
276
          endif
277
       endif
278
    endif
279

280
  end subroutine check_gs2_diagnostics
×
281

282
  !> Initialise this module and all its dependencies, including defining NetCDF
283
  !> vars, call real_init, which calls read_parameters; broadcast all the
284
  !> different write flags.
285
  subroutine init_gs2_diagnostics (list, nstep, gs2_diagnostics_config_in, header)
×
286
    use theta_grid, only: init_theta_grid
287
    use kt_grids, only: init_kt_grids, ntheta0, naky
288
    use run_parameters, only: init_run_parameters
289
    use species, only: init_species, nspec
290
    use dist_fn, only: init_dist_fn
291
    use init_g, only: init_init_g
292
    use gs2_io, only: init_gs2_io
293
    use gs2_heating, only: init_htype
294
    use collisions, only: collision_model_switch, init_lorentz_error
295
    use collisions, only: collision_model_lorentz, collision_model_lorentz_test
296
    use mp, only: broadcast, mp_abort
297
    use gs2_save, only: restart_writable
298
    use normalisations, only: init_normalisations
299
    use diagnostics_final_routines, only: init_par_filter
300
    use diagnostics_velocity_space, only: init_weights, setup_legendre_transform
301
    use standard_header, only: standard_header_type
302
    implicit none
303
    !> If true, this is a "list-mode run" and so turn off
304
    !> [[gs2_diagnostics_knobs:print_flux_line]] and
305
    !> [[gs2_diagnostics_knobs:print_line]] if set
306
    logical, intent (in) :: list
307
    !> Maximum number of steps to take
308
    integer, intent (in) :: nstep
309
    !> Input parameters for this module
310
    type(diagnostics_config_type), intent(in), optional :: gs2_diagnostics_config_in
311
    !> Header for files with build and run information
312
    type(standard_header_type), intent(in), optional :: header
313
    ! Actual value for optional `header` input
314
    type(standard_header_type) :: local_header
×
315
    integer :: nmovie_tot
316

317
    if (present(header)) then
×
318
      local_header = header
×
319
    else
320
      local_header = standard_header_type()
×
321
    end if
322

323
    if (initialized) return
×
324
    initialized = .true.
×
325

326
    call init_normalisations
×
327
    call init_theta_grid
×
328
    call init_kt_grids
×
329
    call init_run_parameters
×
330
    call init_species
×
331
    call init_init_g
×
332
    call init_dist_fn
×
333

334
    call real_init (list, gs2_diagnostics_config_in, header=local_header)
×
335

336
    nmovie_tot = nstep/nmovie
×
337

338
    ! initialize weights for less accurate integrals used
339
    ! to provide an error estimate for v-space integrals (energy and untrapped)
340
    if (write_verr) then
×
341
       call init_weights
×
342
       call setup_legendre_transform
×
343
    end if
344

345
    ! allocate heating diagnostic data structures
346
    if (write_heating) then
×
347
       allocate (h_hist(0:navg-1))
×
348
       call init_htype (h_hist,  nspec)
×
349

350
       allocate (hk_hist(ntheta0,naky,0:navg-1))
×
351
       call init_htype (hk_hist, nspec)
×
352

353
       call init_htype (h,  nspec)
×
354

355
       allocate (hk(ntheta0, naky))
×
356
       call init_htype (hk, nspec)
×
357
    else
358
       allocate (h_hist(0))
×
359
       allocate (hk(1,1))
×
360
       allocate (hk_hist(1,1,0))
×
361
    end if
362

363
    !GGH Allocate density and velocity perturbation diagnostic structures
364
    if (write_jext) then
×
365
       allocate (j_ext_hist(ntheta0, naky,0:navg-1))
×
366
       j_ext_hist = 0
×
367
    end if
368

369
    call init_gs2_io (append_old, make_movie, &
370
         write_correlation_extend, &
371
         write_phi_over_time, write_apar_over_time, write_bpar_over_time, &
372
         nout, ob_midplane=ob_midplane, header=local_header)
×
373

374
    call broadcast (nout)
×
375

376
    if (write_cerr) then
×
377
       if (collision_model_switch == collision_model_lorentz .or. &
×
378
            collision_model_switch == collision_model_lorentz_test) then
379
          call init_lorentz_error
×
380
       else
381
          write_cerr = .false.
×
382
       end if
383
    end if
384

385
    if(.not. allocated(pflux_avg)) then
×
386
       allocate (pflux_avg(nspec), qflux_avg(nspec), heat_avg(nspec), vflux_avg(nspec))
×
387
       pflux_avg = 0.0 ; qflux_avg = 0.0 ; heat_avg = 0.0 ; vflux_avg = 0.0
×
388
    endif
389

390
    call check_restart_file_writeable(file_safety_check, save_for_restart, save_distfn)
×
391

392
    !Setup the parallel fft if we're writing/using the parallel spectrum
393
    if (write_kpar .or. write_gs) call init_par_filter
×
394
  end subroutine init_gs2_diagnostics
×
395

396
  !> Check if we can write the restart files
397
  !>
398
  !> If restart files aren't writable, aborts if `need_restart` is
399
  !> true, or sets `extra_files` to false. Also prints a warning in
400
  !> the second case.
401
  subroutine check_restart_file_writeable(check_writable, need_restart, extra_files)
×
402
    use init_g, only: get_restart_dir
403
    use gs2_save, only: restart_writable
404
    use mp, only: proc0, mp_abort, broadcast
405
    implicit none
406
    !> Has the user requested this check
407
    logical, intent(in) :: check_writable
408
    !> Has the user requested restart files
409
    logical, intent(in) :: need_restart
410
    !> Has the user requested distribution function to be written
411
    logical, intent(inout) :: extra_files
412

413
    ! Error message from trying to open restart file
414
    character(len=:), allocatable :: error_message
×
415
    ! GS2 error message to show user
416
    character(len=:), allocatable :: abort_message
×
417

418
    ! Assume everything's fine if we're not checking, or if it's not needed
419
    if (.not. check_writable) return
×
420
    if (.not. (need_restart .or. extra_files)) return
×
421

422
    ! If we can write the restart files, we're done
423
    if (restart_writable(error_message=error_message)) return
×
424

425
    abort_message = " Cannot write restart files! Error message was:" // new_line('a') &
426
         // "    " // error_message // new_line('a') &
427
         // " Please check that `init_g_knobs::restart_dir = " // trim(get_restart_dir()) &
428
         // "` exists and has the correct permissions."
×
429

430
    ! User has requested restart files, but we can't write them => abort
431
    if (need_restart) then
×
432
      abort_message = abort_message // new_line('a') // "    --> Aborting"
×
433
      call mp_abort(trim(abort_message), to_screen=.true.)
×
434
    end if
435

436
    ! If it's just a case of save_distfn, then we can carry on but
437
    ! disable `save_distfn` and print a useful mesasge
438
    if (extra_files) then
×
439
      if(proc0) write(*, '(a, a, a)') abort_message, new_line('a'), "    --> Setting `save_distfn = F`"
×
440
      extra_files = .false.
×
441
    endif
442
  end subroutine check_restart_file_writeable
×
443

444
  !> Read the input parameters; open the various text output files according to
445
  !> the relevant input flags; allocate and zero the module-level flux arrays,
446
  !> [[gs2_diagnostics_knobs:omega]] and [[gs2_diagnostics_knobs:omegaavg]]
447
  subroutine real_init(is_list_run, gs2_diagnostics_config_in, header)
×
448
    use run_parameters, only: has_apar
449
    use file_utils, only: open_output_file, get_unused_unit
450
    use kt_grids, only: naky, ntheta0
451
    use mp, only: proc0
452
    use standard_header, only: standard_header_type
453
    use diagnostics_fluxes, only: init_diagnostics_fluxes
454
    use diagnostics_kinetic_energy_transfer, only: init_diagnostics_kinetic_energy_transfer
455
    use diagnostics_nonlinear_convergence, only: init_nonlinear_convergence
456
    implicit none
457
    !> If true, this is a "list-mode run" and so turn off
458
    !> [[gs2_diagnostics_knobs:print_flux_line]] and
459
    !> [[gs2_diagnostics_knobs:print_line]] if set
460
    logical, intent (in) :: is_list_run
461
    !> Input parameters for this module
462
    type(diagnostics_config_type), intent(in), optional :: gs2_diagnostics_config_in
463
    !> Header for files with build and run information
464
    type(standard_header_type), intent(in), optional :: header
465
    ! Actual value for optional `header` input
466
    type(standard_header_type) :: local_header
×
467

468
    if (present(header)) then
×
469
      local_header = header
×
470
    else
471
      local_header = standard_header_type()
×
472
    end if
473

474
    call read_parameters(is_list_run, gs2_diagnostics_config_in)
×
475

476
    if (proc0) then
×
477
       if (write_ascii) then
×
478
          call open_output_file (out_unit, ".out")          
×
479
       end if
480

481
       if (write_cross_phase .and. write_ascii) then
×
482
          call open_output_file (phase_unit, ".phase")
×
483
       end if
484

485
       if (write_heating .and. write_ascii) then
×
486
          call open_output_file (heat_unit, ".heat")
×
487
          call open_output_file (heat_unit2, ".heat2")
×
488
       end if
489

490
       if (write_verr .and. write_ascii) then
×
491
          call open_output_file (res_unit, ".vres")
×
492
          call open_output_file (lpc_unit, ".lpc")
×
493
          if (write_max_verr) call open_output_file (res_unit2, ".vres2")
×
494
       end if
495

496
       if (write_cerr .and. write_ascii) then
×
497
          call open_output_file (cres_unit, ".cres")
×
498
       end if
499

500
       if (write_parity .and. write_ascii) then
×
501
          call open_output_file (parity_unit, ".parity")
×
502
       end if
503

504
       if (write_g .and. write_ascii) then
×
505
          call open_output_file(dist_unit, ".dist")
×
506
       end if
507

508
       if (write_gyx .and. write_ascii) then
×
509
          call open_output_file(yxdist_unit, ".yxdist")
×
510
       end if
511

512
       !GGH J_external, only if A_parallel is being calculated.
513
       if (write_jext .and. has_apar) then
×
514
          if (write_ascii) then
×
515
             call open_output_file (jext_unit, ".jext")
×
516
          end if
517
       else
518
          write_jext = .false.
×
519
       end if
520

521
       if (write_ascii) then
×
522
          write (unit=out_unit, fmt='(a)') local_header%to_string(file_description="GS2 output file")
×
523
       end if
524

525
       allocate (omegahist(0:navg-1,ntheta0,naky))
×
526
       omegahist = 0.0
×
527
       if (use_nonlin_convergence) call init_nonlinear_convergence(conv_nstep_av, nwrite)
×
528

529
    end if
530

531
    ! Needed to allow us to use common_calculate_fluxes later
532
    call init_diagnostics_fluxes()
×
533

534
    if (write_kinetic_energy_transfer) call init_diagnostics_kinetic_energy_transfer
×
535
    if (.not. allocated(omega)) then
×
536
       allocate(omega(ntheta0, naky))
×
537
       allocate(omegaavg(ntheta0, naky))
×
538
       omega=0
×
539
       omegaavg=0
×
540
    end if
541

542
  end subroutine real_init
×
543

544
  !> Read the input parameters for the diagnostics module
545
  subroutine read_parameters (is_list_run, gs2_diagnostics_config_in, warn_nonfunctional)
×
546
    use diagnostics_configuration, only: warn_about_nonfunctional_selection, diagnostics_config
547
    use run_parameters, only: has_phi
548
    use antenna, only: no_driver
549
    use nonlinear_terms, only: nonlin
550
    use collisions, only: heating, use_le_layout, set_heating
551
    use mp, only: proc0
552
    use optionals, only: get_option_with_default
553
    implicit none
554
    !> If true, this is a "list-mode run" and so turn off
555
    !> [[gs2_diagnostics_knobs:print_flux_line]] and
556
    !> [[gs2_diagnostics_knobs:print_line]] if set
557
    logical, intent (in) :: is_list_run
558
    !> Configuration for this module, can be used to set new default values or
559
    !> avoid reading the input file
560
    type(diagnostics_config_type), intent(in), optional :: gs2_diagnostics_config_in
561
    logical, intent(in), optional :: warn_nonfunctional
562
    logical :: write_zonal_transfer, write_upar_over_time, write_tperp_over_time
563
    logical :: write_ntot_over_time, write_density_over_time, write_collisional
564
    logical :: serial_netcdf4
565

566
    if (present(gs2_diagnostics_config_in)) diagnostics_config = gs2_diagnostics_config_in
×
567

568
    call diagnostics_config%init(name = 'gs2_diagnostics_knobs', requires_index = .false.)
×
569

570
    ! Print some health warnings if switches are not their default
571
    ! values and are not available in this diagnostics module
572
    if (get_option_with_default(warn_nonfunctional, .true.)) then
×
573
       call warn_about_nonfunctional_selection(diagnostics_config%serial_netcdf4, "serial_netcdf4")
×
574
       call warn_about_nonfunctional_selection(.not. diagnostics_config%write_any, "write_any")
×
575
       call warn_about_nonfunctional_selection(diagnostics_config%write_collisional, "write_collisional")
×
576
       call warn_about_nonfunctional_selection(diagnostics_config%write_density_over_time, "write_density_over_time")
×
577
       call warn_about_nonfunctional_selection(diagnostics_config%write_ntot_over_time, "write_ntot_over_time")
×
578
       call warn_about_nonfunctional_selection(diagnostics_config%write_tperp_over_time, "write_tperp_over_time")
×
579
       call warn_about_nonfunctional_selection(diagnostics_config%write_upar_over_time, "write_upar_over_time")
×
580
       call warn_about_nonfunctional_selection(diagnostics_config%write_zonal_transfer, "write_zonal_transfer")
×
581
    end if
582

583
    ! Copy out internal values into module level parameters
584
    associate(self => diagnostics_config)
585
#include "diagnostics_copy_out_auto_gen.inc"
586
    end associate
587

588
    !CMR, 12/8/2014:
589
    ! Ensure write_full_moments_notgc=.false. if (write_moments .and. ob_midplane)
590
    ! to avoid a conflict.  
591
    ! FIXME: These two diagnostics do almost the same thing. Do we actually want both?
592
    if (write_moments .and. ob_midplane) write_full_moments_notgc=.false.
×
593

594
    !Override flags
595
    if (write_max_verr) write_verr = .true.
×
596

597
    ! The collision_error method assumes we have setup the lz layout.
598
    if (use_le_layout) write_cerr = .false.
×
599
    if (.not. nonlin) use_nonlin_convergence = .false.
×
600

601
    print_summary = (is_list_run .and. (print_line .or. print_flux_line))
×
602

603
    if (is_list_run) then
×
604
       print_line = .false.
×
605
       print_flux_line = .false.
×
606
    end if
607

608
    if (no_driver .and. write_lorentzian) then
×
609
      write(*, "(a)") "WARNING: 'write_lorentzian = .true.' but antenna not enabled. Turning off 'write_lorentzian'"
×
610
      write_lorentzian = .false.
×
611
    end if
612

613
    !These don't store any data if we don't have phi so don't bother
614
    !calculating it.
615
    if(.not. has_phi) write_symmetry = .false.
×
616
    if(.not. has_phi) write_nl_flux_dist = .false.
×
617
    if(.not. has_phi) write_correlation = .false.
×
618
    if(.not. has_phi) write_correlation_extend = .false.
×
619

620
    if (.not. save_for_restart) nsave = -1
×
621
    write_avg_moments = write_avg_moments
×
622

623
    if (write_heating .and. .not. heating) then
×
624
       if (proc0) write(*,'("Warning: Disabling write_heating as collisions:heating is false.")')
×
625
       write_heating = .false.
×
626
    else if (heating .and. .not. write_heating) then
×
627
       call set_heating(.false.)
×
628
    end if
629

630
    write_any = write_line .or. write_omega     .or. write_omavg &
631
         .or. write_flux_line .or. write_fluxes .or. write_fluxes_by_mode  &
632
         .or. write_kpar   .or. write_heating     .or. write_lorentzian  .or. write_gs
×
633
    write_any_fluxes = write_flux_line .or. print_flux_line .or. write_fluxes .or. write_fluxes_by_mode
×
634
    dump_any = make_movie .or. print_summary .or. write_full_moments_notgc
×
635
  end subroutine read_parameters
×
636

637
  !> Finalise the diagnostics module, writing final timestep diagnostics and
638
  !> closing any open files
639
  subroutine finish_gs2_diagnostics
×
640
    use mp, only: proc0
641
    use fields_arrays, only: phinew, bparnew
642
    use gs2_io, only: nc_finish
643
    use antenna, only: dump_ant_amp
644
    use gs2_time, only: user_time
645
    use unit_tests, only: debug_message
646
    use diagnostics_configuration, only: diagnostics_config
647
    use diagnostics_final_routines, only: do_write_final_fields, do_write_kpar
648
    use diagnostics_final_routines, only: do_write_final_epar, do_write_final_db
649
    use diagnostics_final_routines, only: do_write_final_antot, do_write_final_moments
650
    use diagnostics_final_routines, only: do_write_gs
651
    use diagnostics_velocity_space, only: collision_error, finish_legendre_transform, finish_weights
652
    use diagnostics_fields, only: get_phi0
653
    implicit none
654

655
    call debug_message(3, 'gs2_diagnostics::finish_gs2_diagnostics &
656
         & starting')
×
657

658
    if (write_gyx) call do_write_fyx (yxdist_unit, phinew, bparnew)
×
659
    if (write_g) call do_write_f (dist_unit)
×
660
    if (write_cerr) call collision_error (cres_unit, phinew, bparnew)
×
661
    if (write_verr) then
×
662
       call finish_weights
×
663
       call finish_legendre_transform
×
664
    end if
665

666
    ! Close some of the open ascii output files
667
    call close_files
×
668

669
    if (proc0) then
×
670
       if (write_eigenfunc) call do_write_eigenfunc(get_netcdf_file_id(), write_ascii)
×
671
       if (write_final_fields) call do_write_final_fields(write_ascii, &
×
672
            file_id=get_netcdf_file_id())
×
673
       if (write_kpar) call do_write_kpar(write_ascii)
×
674
       if (write_final_epar) call do_write_final_epar(write_ascii, &
×
675
            file_id=get_netcdf_file_id())
×
676

677
       ! definition here assumes we are not using wstar_units
678
       if (write_final_db) call do_write_final_db(write_ascii)
×
679
    end if
680

681
    !Note pass in phase factor phi0 which may not be initialised
682
    !this is ok as phi0 will be set in routine if not already set
683
    if (write_final_moments) call do_write_final_moments(get_phi0(), &
×
684
         use_normalisation=write_eigenfunc, write_text=write_ascii, &
685
         file_id=get_netcdf_file_id())
×
686

687
    if (write_final_antot) call do_write_final_antot(write_ascii, &
×
688
         file_id=get_netcdf_file_id())
×
689

690
    call save_restart_dist_fn(save_for_restart, save_distfn, &
691
                              save_glo_info_and_grids=save_glo_info_and_grids, &
692
                              save_velocities=save_velocities, &
693
                              user_time=user_time)
×
694

695
    !Finalise the netcdf file
696
    call nc_finish
×
697

698
    if (proc0) call dump_ant_amp
×
699

700
    if (write_gs) call do_write_gs(write_ascii)
×
701

702
    if (proc0) call do_write_geom
×
703

704
    !Now tidy up
705
    call deallocate_arrays
×
706

707
    wtmp_old = 0. ; nout = 1 ; nout_movie = 1 ; nout_big = 1
×
708
    initialized = .false.
×
709
    call diagnostics_config%reset()
×
710
  end subroutine finish_gs2_diagnostics
×
711

712
  !> Save some extra information in final restart files
713
  subroutine save_restart_dist_fn(save_for_restart, save_distfn, &
×
714
                                  save_glo_info_and_grids, &
715
                                  save_velocities, user_time, fileopt_base)
716
    use run_parameters, only: has_phi, has_apar, has_bpar
717
    use collisions, only: vnmult
718
    use gs2_save, only: gs2_save_for_restart
719
    use gs2_time, only: code_dt, code_dt_prev1, code_dt_prev2, code_dt_max
720
    use fields_arrays, only: phinew, bparnew
721
    use dist_fn_arrays, only: gnew, g_adjust, to_g_gs2, from_g_gs2
722
    use optionals, only: get_option_with_default
723
    !> See [[gs2_diagnostics_knobs:save_for_restart]]
724
    logical, intent(in) :: save_for_restart
725
    !> See [[gs2_diagnostics_knobs:save_distfn]]
726
    logical, intent(in) :: save_distfn
727
    !> See [[gs2_diagnostics_knobs:save_glo_info_and_grids]]
728
    logical, intent(in) :: save_glo_info_and_grids
729
    !> See [[gs2_diagnostics_knobs:save_velocities]]
730
    logical, intent(in) :: save_velocities
731
    !> Current simulation time
732
    real, intent(in) :: user_time
733
    !> Optional string to add to file name
734
    character(len=*), intent(in), optional :: fileopt_base
735
    character(len=:), allocatable :: fileopt
×
736

737
    fileopt = get_option_with_default(fileopt_base, "")
×
738

739
    if (save_for_restart) then
×
740
       call gs2_save_for_restart(gnew, user_time, vnmult, &
741
            has_phi, has_apar, has_bpar, &
742
            code_dt, code_dt_prev1, code_dt_prev2, code_dt_max, &
743
            save_glo_info_and_grids=save_glo_info_and_grids, &
744
            save_velocities=save_velocities, &
745
            fileopt = fileopt)
×
746
    end if
747

748
    if (save_distfn) then
×
749
       !Convert h to distribution function
750
       call g_adjust(gnew, phinew, bparnew, direction = from_g_gs2)
×
751

752
       !Save dfn, fields and velocity grids to file
753
       call gs2_save_for_restart(gnew, user_time, vnmult, &
754
            has_phi, has_apar, has_bpar, &
755
            code_dt, code_dt_prev1, code_dt_prev2, code_dt_max, &
756
            fileopt= fileopt//".dfn", &
757
            save_glo_info_and_grids=save_glo_info_and_grids, &
758
            save_velocities=save_velocities)
×
759

760
       !Convert distribution function back to h
761
       call g_adjust(gnew, phinew, bparnew, direction = to_g_gs2)
×
762
    end if
763
  end subroutine save_restart_dist_fn
×
764

765
  !> Deallocate the [[gs2_diagnostics]] module-level arrays
766
  subroutine deallocate_arrays
×
767
    use mp, only: proc0
768
    use gs2_heating, only: del_htype
769
    use diagnostics_fluxes, only: finish_diagnostics_fluxes
770
    use diagnostics_kinetic_energy_transfer, only: finish_diagnostics_kinetic_energy_transfer
771
    use diagnostics_nonlinear_convergence, only: finish_nonlinear_convergence
772
    implicit none
773

774
    if (write_heating) then
×
775
       call del_htype (h)
×
776
       call del_htype (h_hist)
×
777
       call del_htype (hk_hist)
×
778
       call del_htype (hk)
×
779
    end if
780

781
    call finish_diagnostics_fluxes()
×
782
    call finish_diagnostics_kinetic_energy_transfer()
×
783
    if (allocated(h_hist)) deallocate (h_hist, hk_hist, hk)
×
784
    if (allocated(j_ext_hist)) deallocate (j_ext_hist)
×
785
    if (proc0 .and. allocated(omegahist)) deallocate (omegahist)
×
786
    if (allocated(pflux_tormom)) deallocate (pflux_tormom) 
×
787

788
    if (allocated(pflux_avg)) deallocate (pflux_avg, qflux_avg, heat_avg, vflux_avg)
×
789
    if (allocated(omega)) deallocate(omega, omegaavg)
×
790
    if (use_nonlin_convergence) call finish_nonlinear_convergence
×
791

792
    if (proc0 .and. allocated(domega)) deallocate(domega)
×
793
  end subroutine deallocate_arrays
×
794

795
  !> Close various text output files
796
  subroutine close_files
×
797
    use file_utils, only: close_output_file
798
    use mp, only: proc0
799
    implicit none
800

801
    if(.not.proc0) return
×
802
    if (write_ascii .and. write_parity) call close_output_file (parity_unit)
×
803
    if (write_ascii .and. write_verr) then
×
804
       call close_output_file (res_unit)
×
805
       call close_output_file (lpc_unit)
×
806
       if (write_max_verr) call close_output_file (res_unit2)
×
807
    end if
808
    if (write_ascii .and. write_cerr) call close_output_file(cres_unit)
×
809
    if (write_ascii .and. write_g) call close_output_file(dist_unit)
×
810
    if (write_ascii .and. write_gyx) call close_output_file(yxdist_unit)
×
811
    if (write_ascii) call close_output_file (out_unit)
×
812
    if (write_ascii .and. write_cross_phase) call close_output_file (phase_unit)
×
813
    if (write_ascii .and. write_heating) call close_output_file (heat_unit)
×
814
    if (write_ascii .and. write_heating) call close_output_file (heat_unit2)
×
815
    if (write_ascii .and. write_jext) call close_output_file (jext_unit)
×
816
  end subroutine close_files
817

818
  !> Calculate and write the various diagnostic quantities.
819
  !>
820
  !> This is the main routine for the "old" diagnostics.
821
  subroutine loop_diagnostics (istep, exit, debopt, force)
×
822
    use, intrinsic :: iso_fortran_env, only: output_unit
823
    use species, only: nspec, spec, has_electron_species
824
    use kt_grids, only: naky, ntheta0
825
    use run_parameters, only: nstep, has_phi, has_apar, has_bpar
826
    use fields_arrays, only: phinew, bparnew
827
    use collisions, only: ncheck, vary_vnew
828
    use mp, only: proc0, broadcast
829
    use gs2_time, only: user_time
830
    use gs2_io, only: nc_qflux, nc_vflux, nc_pflux, nc_pflux_tormom, nc_exchange, nc_final_fields, nc_sync, get_netcdf_movie_file_id
831
    use antenna, only: antenna_w
832
    use optionals, only: get_option_with_default
833
    use diagnostics_printout, only: write_phi_fluxes_to_unit, write_fluxes_to_unit
834
    use diagnostics_fluxes, only: common_calculate_fluxes, qheat, qmheat, qbheat, &
835
         pflux, vflux, vflux_par, vflux_perp, pflux_tormom, vflux0, vflux1, pmflux, vmflux, &
836
         pbflux, vbflux, exchange
837
    use diagnostics_velocity_space, only: collision_error
838
    use optionals, only: get_option_with_default
839
    use warning_helpers, only: is_not_zero, not_exactly_equal
840
    use volume_averages, only: average_all, average_ky
841
    implicit none
842
    !> Current timestep
843
    integer, intent (in) :: istep
844
    !> If true, simulation should stop (for example, because it has converged)
845
    logical, intent (out) :: exit
846
    !> If true, turn on some debug prints
847
    logical, intent (in), optional:: debopt
848
    logical, intent(in), optional :: force
849
    real, dimension (ntheta0, naky) :: phitot
×
850
    real :: phi2, apar2, bpar2
851
    real :: t
852
    integer :: ik, it, write_mod
853
    complex, save :: wtmp_new !This shouldn't need to be given the save property
854
    real, dimension (ntheta0, nspec) :: x_qmflux
×
855
    real, dimension (nspec) ::  heat_fluxes,  part_fluxes, mom_fluxes, parmom_fluxes, perpmom_fluxes, part_tormom_fluxes
×
856
    real, dimension (nspec) :: mheat_fluxes, mpart_fluxes, mmom_fluxes
×
857
    real, dimension (nspec) :: bheat_fluxes, bpart_fluxes, bmom_fluxes
×
858
    real, dimension (nspec) :: energy_exchange
×
859
    real, dimension (nspec) ::  heat_par,  heat_perp
×
860
    real, dimension (nspec) :: mheat_par, mheat_perp
×
861
    real, dimension (nspec) :: bheat_par, bheat_perp
×
862
    real :: hflux_tot, zflux_tot, vflux_tot
863
    logical :: do_force, debug
864
    real, save :: t_old = 0.
865
    integer, save :: istep_last = -1
866
    debug = get_option_with_default(debopt, .false.)
×
867

868
    !Set the current time
869
    t = user_time
×
870
    !Always skip if we've already done this step
871
    if (istep == istep_last) return
×
872

873
    do_force = get_option_with_default(force, .false.)
×
874

875
    exit = .false.
×
876

877
    call do_get_omega(istep,debug,exit)
×
878

879
    if (write_heating) call heating (istep, navg, h, hk, h_hist, hk_hist)
×
880

881
    if (make_movie .and. mod(istep,nmovie) == 0) call do_write_movie(t)
×
882

883
    if (write_gyx .and. mod(istep,nmovie) == 0) call do_write_fyx (yxdist_unit, phinew, bparnew)
×
884

885
    if (vary_vnew) then
×
886
       write_mod = mod(istep,ncheck)
×
887
    else
888
       write_mod = mod(istep,nwrite)
×
889
    end if
890

891
    if (write_verr .and. write_mod == 0) call do_write_verr
×
892

893
    if (debug) write(6,*) "loop_diagnostics: call update_time"
×
894

895
    !########################################################
896
    !The rest of the routine only happens every nwrite steps
897
    !########################################################
898
    if (mod(istep,nwrite) /= 0 .and. .not. exit .and. .not. do_force) return
×
899

900
    ! If we get here then we're doing a full set of diagnostics
901
    ! so store the step
902
    istep_last = istep
×
903

904
    if (write_g) call do_write_f (dist_unit)
×
905

906
    ! Note this also returns phi2, apar2, bpar2 and phitot for other diagnostics
907
    if (proc0) call do_write_ncloop(t, istep, phi2, apar2, bpar2, phitot)
×
908

909
    if (print_line) call do_print_line(phitot)
×
910

911
    if (write_moments) call do_write_moments
×
912

913
    if (write_cross_phase .and. has_electron_species(spec) .and. write_ascii) call do_write_crossphase(t)
×
914

915
    !###########################
916
    ! The following large section could do with being moved to separate routines but
917
    !     at the moment its all quite interlinked which makes this hard.
918
    !###########################
919

920
    !Zero various arrays which may or may not get filled
921
    part_fluxes = 0.0 ; mpart_fluxes = 0.0 ; bpart_fluxes = 0.0
×
922
    heat_fluxes = 0.0 ; mheat_fluxes = 0.0 ; bheat_fluxes = 0.0
×
923
    mom_fluxes = 0.0 ; mmom_fluxes = 0.0 ; bmom_fluxes = 0.0  
×
924
    part_tormom_fluxes = 0.0
×
925
    energy_exchange = 0.0
×
926

927
    if (debug) write(6,*) "loop_diagnostics: -1"
×
928

929
    if (write_any_fluxes) then
×
930
       call common_calculate_fluxes()
×
931
       if (proc0) then
×
932
          if (has_phi) then
×
933
             call average_all(qheat(:, :, :, 1), heat_fluxes)
×
934
             call average_all(qheat(:, :, :, 2), heat_par)
×
935
             call average_all(qheat(:, :, :, 3), heat_perp)
×
936
             call average_all(pflux, part_fluxes)
×
937
             call average_all(pflux_tormom, part_tormom_fluxes)
×
938
             call average_all(vflux, mom_fluxes)
×
939
             call average_all(vflux_par, parmom_fluxes)
×
940
             call average_all(vflux_perp, perpmom_fluxes)
×
941
             call average_all(exchange, energy_exchange)
×
942
          end if
943
          if (has_apar) then
×
944
             call average_all(qmheat(:, :, :, 1), mheat_fluxes)
×
945
             call average_all(qmheat(:, :, :, 2), mheat_par)
×
946
             call average_all(qmheat(:, :, :, 3), mheat_perp)
×
947
             call average_all(pmflux, mpart_fluxes)
×
948
             call average_all(vmflux, mmom_fluxes)
×
949
             call average_ky(qmheat(:, :, :, 1), x_qmflux)
×
950
          end if
951
          if (has_bpar) then
×
952
             call average_all(qbheat(:, :, :, 1), bheat_fluxes)
×
953
             call average_all(qbheat(:, :, :, 2), bheat_par)
×
954
             call average_all(qbheat(:, :, :, 3), bheat_perp)
×
955
             call average_all(pbflux, bpart_fluxes)
×
956
             call average_all(vbflux, bmom_fluxes)
×
957
          end if
958
          pflux_avg = pflux_avg + (part_fluxes + mpart_fluxes + bpart_fluxes)*(t-t_old)
×
959
          qflux_avg = qflux_avg + (heat_fluxes + mheat_fluxes + bheat_fluxes)*(t-t_old)
×
960
          vflux_avg = vflux_avg + (mom_fluxes + mmom_fluxes + bmom_fluxes)*(t-t_old)
×
961
          if (write_heating) heat_avg = heat_avg + h%imp_colls*(t-t_old)
×
962
          !          t_old = t
963
       end if
964
    end if
965

966
    if (proc0 .and. print_flux_line) then
×
967
      if (has_phi) then
×
968
        call write_phi_fluxes_to_unit(output_unit, t, phi2, heat_fluxes, energy_exchange)
×
969
      end if
970
      if (has_apar) then
×
971
        call write_fluxes_to_unit(output_unit, t, apar2, "apar", mheat_fluxes)
×
972
      end if
973
      if (has_bpar) then
×
974
        call write_fluxes_to_unit(output_unit, t, bpar2, "bpar", bheat_fluxes)
×
975
      end if
976
    end if
977

978
    ! Check for convergence
979
    if (use_nonlin_convergence) call check_nonlin_convergence(istep, heat_fluxes(1), exit)
×
980
    if (debug) write(6,*) "loop_diagnostics: -1"
×
981

982
    if (.not. (write_any .or. dump_any)) then
×
983
       ! We have to increment the number of outputs written to file
984
       ! before leaving. Usually we do this at the end of the routine
985
       ! so we must make sure we do this here before leaving early.
986
       nout = nout + 1
×
987
       return
×
988
    end if
989

990
    if (debug) write(output_unit, *) "loop_diagnostics: -2"
×
991

992
    if (proc0 .and. write_any) then
×
993
       if (write_ascii) then
×
994
         write (unit=out_unit, fmt=*) 'time=', t
×
995
         if (write_heating) call do_write_heating(t, heat_unit, heat_unit2, h)
×
996
         if (write_jext) call do_write_jext(t,istep)
×
997
       end if
998

999
       hflux_tot = 0.
×
1000
       zflux_tot = 0.
×
1001
       vflux_tot = 0.
×
1002
       if (has_phi) then
×
1003
         hflux_tot = sum(heat_fluxes)
×
1004
         vflux_tot = sum(mom_fluxes)
×
1005
         zflux_tot = sum(part_fluxes*spec%z)
×
1006
       end if
1007
       if (has_apar) then
×
1008
         hflux_tot = hflux_tot + sum(mheat_fluxes)
×
1009
         vflux_tot = vflux_tot + sum(mmom_fluxes)
×
1010
         zflux_tot = zflux_tot + sum(mpart_fluxes*spec%z)
×
1011
       end if
1012
       if (has_bpar) then
×
1013
         hflux_tot = hflux_tot + sum(bheat_fluxes)
×
1014
         vflux_tot = vflux_tot + sum(bmom_fluxes)
×
1015
         zflux_tot = zflux_tot + sum(bpart_fluxes*spec%z)
×
1016
       end if
1017

1018
       if (write_flux_line .and. write_ascii) then
×
1019
         if (has_phi) then
×
1020
           call write_phi_fluxes_to_unit(out_unit, t, phi2, &
1021
                heat_fluxes, energy_exchange, part_fluxes, mom_fluxes)
×
1022
         end if
1023
         if (write_lorentzian) then
×
1024
           wtmp_new = antenna_w()
×
1025
           if (is_not_zero(real(wtmp_old)) .and. not_exactly_equal(wtmp_new, wtmp_old)) &
×
1026
                write (unit=out_unit, fmt="('w= ',e17.10, ' amp= ',e17.10)") real(wtmp_new), sqrt(2.*apar2)
×
1027
           wtmp_old = wtmp_new
×
1028
         end if
1029
         if (has_apar) then
×
1030
           call write_fluxes_to_unit(out_unit, t, apar2, "apar", mheat_fluxes, mpart_fluxes)
×
1031
         end if
1032
         if (has_bpar) then
×
1033
           call write_fluxes_to_unit(out_unit, t, bpar2, "bpar", bheat_fluxes, bpart_fluxes)
×
1034
         end if
1035
         write (unit=out_unit, fmt="('t= ',e17.10,' h_tot= ',e11.4, ' z_tot= ',e11.4)") t, hflux_tot, zflux_tot
×
1036
       end if
1037

1038
       if (write_fluxes) then
×
1039
         call nc_qflux (get_netcdf_file_id(), nout, qheat(:,:,:,1), qmheat(:,:,:,1), qbheat(:,:,:,1), &
×
1040
              heat_par, mheat_par, bheat_par, &
×
1041
              heat_perp, mheat_perp, bheat_perp, &
×
1042
              heat_fluxes, mheat_fluxes, bheat_fluxes, x_qmflux, hflux_tot)
×
1043
         call nc_exchange (get_netcdf_file_id(), nout, exchange, energy_exchange)
×
1044
         call nc_vflux (get_netcdf_file_id(), nout, vflux, vmflux, vbflux, &
1045
              mom_fluxes, mmom_fluxes, bmom_fluxes, vflux_tot, &
×
1046
              vflux_par, vflux_perp, vflux0, vflux1)
×
1047
         call nc_pflux (get_netcdf_file_id(), nout, pflux, pmflux, pbflux, &
1048
              part_fluxes, mpart_fluxes, bpart_fluxes, zflux_tot)
×
1049
       end if
1050

1051
       if (write_ascii) then
×
1052
          do ik = 1, naky
×
1053
             do it = 1, ntheta0
×
1054
                if (write_line) call do_write_line(t,it,ik,phitot(it,ik))
×
1055
                if (write_omega) call do_write_omega(it,ik)
×
1056
                if (write_omavg) call do_write_omavg(it,ik)
×
1057
             end do
1058
          enddo
1059
       end if
1060
    endif
1061

1062
    if (write_cerr) call collision_error(cres_unit, phinew, bparnew)
×
1063

1064
    if (write_nl_flux_dist) call do_write_nl_flux_dist(get_netcdf_file_id(), nout)
×
1065

1066
    if (write_kinetic_energy_transfer) call do_write_kinetic_energy_transfer(get_netcdf_file_id(), nout)
×
1067

1068
    if (write_symmetry) call do_write_symmetry(get_netcdf_file_id(), nout)
×
1069

1070
    if (write_correlation) call do_write_correlation(get_netcdf_file_id(), nout)
×
1071

1072
    if (write_correlation_extend .and. istep > nstep/4 .and. mod(istep,nwrite_mult*nwrite)==0) &
×
1073
         call do_write_correlation_extend(get_netcdf_file_id(), t, t_old)
×
1074

1075
    if (write_parity) call do_write_parity(t, parity_unit, write_ascii)
×
1076

1077
    if (write_avg_moments) call do_write_avg_moments
×
1078

1079
    if (write_full_moments_notgc) call do_write_full_moments_notgc
×
1080

1081
    !Now sync the data to file (note doesn't actually sync every call)
1082
    if (proc0) call nc_sync(get_netcdf_file_id(), nout, get_netcdf_movie_file_id(), nout_movie, nc_sync_freq)
×
1083

1084
    !Increment loop counter
1085
    nout = nout + 1
×
1086

1087
    if (write_ascii .and. mod(nout, 10) == 0 .and. proc0) call flush_files
×
1088

1089
    !Update time
1090
    t_old = t
×
1091

1092
    if (debug) write(6,*) "loop_diagnostics: done"
×
1093
  end subroutine loop_diagnostics
1094

1095
  !> Calculate [[gs2_diagnostics_knobs:omegaavg]] for linear simulations or if
1096
  !> [[run_parameters_knobs:eqzip]] is on; otherwise set
1097
  !> [[gs2_diagnostics_knobs:omega]] and [[gs2_diagnostics_knobs:omegaavg]] to
1098
  !> zero
1099
  subroutine do_get_omega(istep,debug,exit)
×
1100
    use mp, only: proc0, broadcast
1101
    use nonlinear_terms, only: nonlin
1102
    use run_parameters, only: ieqzip
1103
    implicit none
1104
    !> The current timestep
1105
    integer, intent(in) :: istep
1106
    !> Turn on some debug messages
1107
    logical, intent(in) :: debug
1108
    !> Returns true if the simulation has converged (see
1109
    !> [[gs2_diagnostics_knobs:omegatol]]) or if a numerical instability has
1110
    !> occurred (see [[gs2_diagnostics_knobs:omegainst]]).
1111
    logical, intent(inout) :: exit
1112

1113
    if (nonlin .and. .not. any(ieqzip)) then
×
1114
       !Make sure we've at least initialised the omega arrays
1115
       !for any later output etc.
1116
       omega = 0.
×
1117
       omegaavg = 0.
×
1118
    else
1119
       if (proc0) then
×
1120
          if (debug) write(6,*) "loop_diagnostics: proc0 call get_omegaavg"
×
1121
          call get_omegaavg (istep, exit, omegaavg, debug)
×
1122
          if (debug) write(6,*) "loop_diagnostics: proc0 done called get_omegaavg"
×
1123
       endif
1124
       call broadcast (exit)
×
1125
    end if
1126

1127
  end subroutine do_get_omega
×
1128

1129
  !> Compute volume averages of the fields and write the fields, field averages,
1130
  !> heating and frequency to the netCDF files
1131
  subroutine do_write_ncloop(time, istep, phi2, apar2, bpar2, phitot)
×
1132
    use gs2_time, only: woutunits, tunits
1133
    use gs2_io, only: nc_loop
1134
    use mp, only: proc0
1135
    use kt_grids, only: ntheta0, naky
1136
    implicit none
1137
    !> Simulation time
1138
    real, intent(in) :: time
1139
    !> Current timestep
1140
    integer, intent(in) :: istep
1141
    !> Fields squared
1142
    real, intent(out) :: phi2, apar2, bpar2
1143
    !> FIXME: add documentation. Needs [[phinorm]] documenting
1144
    real, dimension (:, :), intent(out) :: phitot
1145
    real, dimension (ntheta0, naky) :: ql_metric, growth_rates
×
1146
    phitot = 0.0
×
1147
    if (.not. proc0) return
×
1148

1149
    omega = omegahist(mod(istep,navg),:,:)
×
1150

1151
    call phinorm (phitot)
×
1152

1153
    if (write_ql_metric) then
×
1154
       ! Calculate the instantaneous omega and keep the growth rate.
1155
       growth_rates = aimag(calculate_instantaneous_omega())
×
1156
       ql_metric = calculate_simple_quasilinear_flux_metric_by_k(growth_rates)
×
1157
    else
1158
       ql_metric = 0.0
×
1159
    end if
1160

1161
    call nc_loop (get_netcdf_file_id(), nout, time, &
1162
         phi2, apar2, bpar2, igomega, &
1163
         h, hk, omega, omegaavg, woutunits, tunits, phitot, ql_metric, &
×
1164
         write_omega, write_heating, write_ql_metric)
×
1165
  end subroutine do_write_ncloop
1166

1167
  !> Write \(\phi, A_\parallel, B_\parallel\) normalised to the value of
1168
  !> \(\phi\) at the outboard midplane
1169
  !>
1170
  !> Writes to text file `<runname>.eigenfunc` and/or netCDF
1171
  subroutine do_write_eigenfunc(file_id, write_text)
×
1172
    use mp, only: proc0
1173
    use file_utils, only: open_output_file, close_output_file
1174
    ! This renaming seems needed to avoid an issue with ifx
1175
    use diagnostics_fields, only: write_eigenfunc_ => write_eigenfunc
1176
    implicit none
1177
    !> NetCDF ID of the file to write to
1178
    integer, intent(in) :: file_id
1179
    !> If true, write text file
1180
    logical, intent(in) :: write_text
1181
    integer :: unit
1182
    if (.not. proc0) return
×
1183
    if (write_text) call open_output_file (unit, ".eigenfunc")
×
1184
    call write_eigenfunc_(write_text, unit, file_id)
×
1185
    if (write_text) close(unit)
×
1186
  end subroutine do_write_eigenfunc
1187

1188
  !> Write some geometry information to text file `<runname>.g`
1189
  subroutine do_write_geom
×
1190
    use mp, only: proc0
1191
    use file_utils, only: open_output_file, close_output_file
1192
    use theta_grid, only: ntgrid, theta, Rplot, Zplot, aplot, Rprime, Zprime, aprime, drhodpsi, qval, shape
1193
    implicit none
1194
    integer :: i, unit
1195

1196
    if(.not.proc0) return
×
1197

1198
    !May want to guard this with if(write_ascii) but not until we provide this
1199
    !data in main netcdf output by default.
1200
    call open_output_file (unit, ".g")
×
1201
    write (unit,fmt="('# shape: ',a)") trim(shape)
×
1202
    write (unit,fmt="('# q = ',e11.4,' drhodpsi = ',e11.4)") qval, drhodpsi
×
1203
    write (unit,fmt="('# theta1             R2                  Z3               alpha4      ', &
1204
         &   '       Rprime5              Zprime6           alpha_prime7 ')")
×
1205
    do i=-ntgrid,ntgrid
×
1206
       write (unit,1000) theta(i),Rplot(i),Zplot(i),aplot(i), &
×
1207
            Rprime(i),Zprime(i),aprime(i)
×
1208
    enddo
1209
    call close_output_file (unit)
×
1210
1000 format(20(1x,1pg18.11))
1211
  end subroutine do_write_geom
1212

1213
  !> Transform \(\phi, A_\parallel, B_\parallel\) to real space, then write to netCDF
1214
  subroutine do_write_movie(time)
×
1215
    use gs2_io, only: nc_loop_movie, get_netcdf_movie_file_id
1216
    use mp, only: proc0
1217
    implicit none
1218
    !> Current simulation time
1219
    real, intent(in) :: time
1220
    if (proc0) call nc_loop_movie(get_netcdf_movie_file_id(), nout_movie, time)
×
1221
    nout_movie = nout_movie + 1
×
1222
  end subroutine do_write_movie
×
1223

1224
  !> Print estimated frequencies and growth rates to screen/stdout
1225
  subroutine do_print_line(phitot)
×
1226
    use mp, only: proc0
1227
    use kt_grids, only: naky, ntheta0, aky, akx, theta0
1228
    use gs2_time, only: woutunits
1229
    implicit none
1230
    !> Total magnitude of all the fields
1231
    real, dimension (:, :), intent(in) :: phitot
1232
    integer :: ik, it
1233

1234
    if(.not.proc0) return
×
1235
    do ik = 1, naky
×
1236
       do it = 1, ntheta0
×
1237
          write (unit=*, fmt="('ky=',1pe9.2, ' kx=',1pe9.2, &
1238
               & ' om=',e9.2,1x,e9.2,' omav=',e9.2,1x,e9.2, &
1239
               & ' phtot=',e9.2,' theta0=',1pe9.2)") &
×
1240
               aky(ik), akx(it), &
×
1241
               real( omega(it,ik)*woutunits(ik)), &
×
1242
               aimag(omega(it,ik)*woutunits(ik)), &
×
1243
               real( omegaavg(it,ik)*woutunits(ik)), &
×
1244
               aimag(omegaavg(it,ik)*woutunits(ik)), &
×
1245
               phitot(it,ik), theta0(it,ik)
×
1246
       end do
1247
    end do
1248
    write (*,*) 
×
1249
  end subroutine do_print_line
1250

1251
  !> Calculate and write the time-averaged antenna current to [[jext_unit]]
1252
  subroutine do_write_jext(time, istep)
×
1253
    use kt_grids, only: ntheta0, naky
1254
    use mp, only: proc0
1255
    use warning_helpers, only: is_not_zero
1256
    implicit none
1257
    !> Current simulation time
1258
    real, intent(in) :: time
1259
    !> Current timestep
1260
    integer, intent(in) :: istep
1261
    integer :: ik, it
1262
    real, dimension(:,:), allocatable ::  j_ext
×
1263

1264
    if (.not.proc0) return
×
1265
    if (.not. write_ascii) return
×
1266
    allocate (j_ext(ntheta0, naky)); j_ext=0.
×
1267
    call calc_jext(istep, j_ext)
×
1268
    do ik=1,naky
×
1269
       do it = 1, ntheta0
×
1270
          if (is_not_zero(j_ext(it, ik))) then
×
1271
             write (unit=jext_unit, fmt="(es12.4,i4,i4,es12.4)")  &
1272
                  time,it,ik,j_ext(it,ik)
×
1273
          endif
1274
       enddo
1275
    enddo
1276
    deallocate(j_ext)
×
1277
  end subroutine do_write_jext
×
1278

1279
  !> Write various moments to netCDF
1280
  subroutine do_write_moments
×
1281
    use gs2_io, only: nc_write_moments
1282
    use theta_grid, only: ntgrid
1283
    use kt_grids, only: naky, ntheta0
1284
    use species, only: nspec
1285
    use diagnostics_moments, only: getmoms
1286
    use mp, only: proc0
1287
    use fields_arrays, only: phinew, bparnew
1288
    implicit none
1289
    complex, dimension (-ntgrid:ntgrid,ntheta0,naky,nspec) :: ntot, density, &
×
1290
         upar, tpar, tperp, qparflux, pperpj1, qpperpj1
×
1291

1292
    call getmoms (phinew, bparnew, ntot, density, upar, tpar, tperp, qparflux, pperpj1, qpperpj1)
×
1293
    if(proc0) call nc_write_moments(get_netcdf_file_id(), nout, ntot, density, upar, tpar, tperp,qparflux, &
×
1294
         pperpj1, qpperpj1, ob_midplane=ob_midplane)
×
1295
  end subroutine do_write_moments
×
1296

1297
  !> Calculate the cross-phase (see [[get_cross_phase]]) and write to the
1298
  !> [[phase_unit]] file
1299
  subroutine do_write_crossphase(time)
×
1300
    use mp, only: proc0
1301
    use diagnostics_turbulence, only: get_cross_phase
1302
    implicit none
1303
    !> Current simulation time
1304
    real, intent(in) :: time
1305
    real :: phase_tot, phase_theta
1306
    if (.not. write_ascii) return
×
1307
    call get_cross_phase (phase_tot, phase_theta)
×
1308
    if (proc0) write (unit=phase_unit, fmt="('t= ',e17.10,' phase_tot= ',e11.4,' phase_theta= ',e11.4)") &
×
1309
         & time, phase_tot, phase_theta
×
1310
  end subroutine do_write_crossphase
1311

1312
  !> Write estimated frequency and growth rates to [[out_unit]] for an individual \((k_y, \theta)\) point
1313
  subroutine do_write_line(time,it,ik,phitot)
×
1314
    use kt_grids, only: aky, akx, theta0
1315
    use gs2_time, only: woutunits
1316
    implicit none
1317
    !> Simulation time
1318
    real, intent(in) :: time, phitot
1319
    !> \(k_y\)- and \(\theta\)-indices
1320
    integer, intent(in) :: ik, it
1321
    if (.not. write_ascii) return
×
1322
    write (out_unit, "('t= ',e17.10,' aky= ',1p,e12.4, ' akx= ',1p,e12.4, &
1323
         &' om= ',1p,2e12.4,' omav= ', 1p,2e12.4,' phtot= ',1p,e12.4,' theta0= ',1p,e12.4)") &
1324
         time, aky(ik), akx(it), &
×
1325
         real( omega(it,ik)*woutunits(ik)), &
×
1326
         aimag(omega(it,ik)*woutunits(ik)), &
×
1327
         real( omegaavg(it,ik)*woutunits(ik)), &
×
1328
         aimag(omegaavg(it,ik)*woutunits(ik)), &
×
1329
         phitot,theta0(it,ik)
×
1330
  end subroutine do_write_line
1331

1332
  !> Write instantaneous growth rate and frequency to [[out_unit]] for an individual \((k_y, \theta)\) point
1333
  subroutine do_write_omega(it,ik)
×
1334
    use gs2_time, only: tunits, woutunits
1335
    implicit none
1336
    !> \(k_y\)- and \(\theta\)-indices
1337
    integer, intent(in) :: it, ik
1338
    if (.not. write_ascii) return
×
1339
    write (out_unit,&
1340
         fmt='(" omega= (",1p,e12.4,",",1p,e12.4,")",t45,"omega/(vt/a)= (",1p,e12.4,",",1p,e12.4,")")') &
1341
         omega(it,ik)/tunits(ik), omega(it,ik)*woutunits(ik)
×
1342
  end subroutine do_write_omega
1343

1344
  !> Write time-averaged growth rate and frequency to [[out_unit]] for an individual \((k_y, \theta)\) point
1345
  subroutine do_write_omavg(it,ik)
×
1346
    use gs2_time, only: tunits, woutunits
1347
    implicit none
1348
    integer, intent(in) :: it, ik
1349
    if (.not. write_ascii) return
×
1350
    write (out_unit,&
1351
         fmt='(" omavg= (",1p,e12.4,",",1p,e12.4,")",t45,"omavg/(vt/a)= (",1p,e12.4,",",1p,e12.4,")")') &
1352
         omegaavg(it,ik)/tunits(ik), omegaavg(it,ik)*woutunits(ik)               
×
1353
  end subroutine do_write_omavg
1354

1355
  !> Write some error estimates.
1356
  !>
1357
  !> Error estimates are calculated by:
1358
  !> - comparing standard integral with less-accurate integral
1359
  !> - monitoring amplitudes of legendre polynomial coefficients
1360
  !>
1361
  !> Only writes to text file, requires [[lpc_unit]] to be `open`
1362
  subroutine do_write_verr
×
1363
    use mp, only: proc0
1364
    use le_grids, only: grid_has_trapped_particles
1365
    use fields_arrays, only: phinew, bparnew
1366
    use gs2_time, only: user_time
1367
    use collisions, only: vnmult
1368
    use species, only: spec
1369
    use diagnostics_velocity_space, only: get_verr, get_gtran
1370
    implicit none
1371
    real, dimension(5,2) :: errest
1372
    integer, dimension (5,3) :: erridx
1373
    real :: geavg, glavg, gtavg
1374

1375
    errest = 0.0; erridx = 0
×
1376

1377
    ! error estimate obtained by comparing standard integral with less-accurate integral
1378
    call get_verr (errest, erridx, phinew, bparnew)
×
1379

1380
    ! error estimate based on monitoring amplitudes of legendre polynomial coefficients
1381
    call get_gtran (geavg, glavg, gtavg, phinew, bparnew)
×
1382

1383
    if (proc0) then
×
1384
       ! write error estimates to .nc file          
1385
       !          call nc_loop_vres (nout, errest_by_mode, lpcoef_by_mode)
1386

1387
       ! write error estimates for ion dist. fn. at outboard midplane with ik=it=1 to ascii files
1388
       if (write_ascii) then
×
1389
          if (grid_has_trapped_particles()) then
×
1390
             write(lpc_unit,"(4(1x,e13.6))") user_time, geavg, glavg, gtavg
×
1391
          else
1392
             write(lpc_unit,"(3(1x,e13.6))") user_time, geavg, glavg
×
1393
          end if
1394
          write(res_unit,"(8(1x,e13.6))") user_time, errest(1,2), errest(2,2), errest(3,2), &
×
1395
               errest(4,2), errest(5,2), vnmult(1)*spec(1)%vnewk, vnmult(2)*spec(1)%vnewk
×
1396
          if (write_max_verr) then
×
1397
             write(res_unit2,"(3(i8),(1x,e13.6),3(i8),(1x,e13.6),3(i8),(1x,e13.6),3(i8),(1x,e13.6),3(i8),(1x,e13.6))") &
1398
                  erridx(1,1), erridx(1,2), erridx(1,3), errest(1,1), &
×
1399
                  erridx(2,1), erridx(2,2), erridx(2,3), errest(2,1), &
×
1400
                  erridx(3,1), erridx(3,2), erridx(3,3), errest(3,1), &
×
1401
                  erridx(4,1), erridx(4,2), erridx(4,3), errest(4,1), &
×
1402
                  erridx(5,1), erridx(5,2), erridx(5,3), errest(5,1)
×
1403
          end if
1404
       end if
1405
    end if
1406
  end subroutine do_write_verr
×
1407

1408
  !> Write the (total) heating diagnostics to [[heat_unit]] and [[heat_unit2]] (per-species)
1409
  subroutine do_write_heating(t, file_unit, file_unit2, h)
×
1410
    use species, only: nspec
1411
    implicit none
1412
    real, intent(in) :: t
1413
    integer, intent(in) :: file_unit, file_unit2
1414
    !> Heating diagnostics
1415
    type(heating_diagnostics), intent (in) :: h
1416

1417
    integer :: is
1418

1419
    !
1420
    ! For case with two species:
1421
    !
1422
    ! Column     Item               
1423
    !   1        time              
1424
    !   2        Energy              
1425
    !   3        dEnergy/dt            
1426
    !   4        J_ant.E             
1427
    !   5        [h_(i+1)*h_*]/2 * C[h_(i+1)] * T_0 for species 1
1428
    !   6        [h_(i+1)*h_*]/2 * C[h_(i+1)] * T_0 for species 2
1429
    !   7       -[h H(h) * T_0]_1
1430
    !   8       -[h H(h) * T_0]_2
1431
    !   9       -[h C(h) * T_0]_1 
1432
    !  10       -[h C(h) * T_0]_2
1433
    !  11        [h w_* h]_1
1434
    !  12        [h w_* h]_2
1435
    !  13        [h * (q dchi/dt - dh/dt * T0)]_1
1436
    !  14        [h * (q dchi/dt - dh/dt * T0)]_2
1437
    !  15      sum (h C(h) * T_0)  in total, as in 5, 6      
1438
    !  16     -sum (h H(h) * T_0)      
1439
    !  17     -sum (h C(h) * T_0)   
1440
    !  18      sum (h w_* h)  
1441
    !  19      sum [h (q dchi/dt - dh/dt * T0)]
1442
    !  20      3 + 4 + 18 + 19
1443
    !  21      (k_perp A)**2
1444
    !  22      B_par**2
1445
    !  23      df_1 ** 2
1446
    !  24      df_2 ** 2
1447
    !  25      h_1 ** 2
1448
    !  26      h_2 ** 2
1449
    !  27      Phi_bar_1 ** 2
1450
    !  28      Phi_bar_2 ** 2
1451
    !
1452
    !
1453
    ! For case with one species:
1454
    !
1455
    ! Column     Item               
1456
    !   1        time              
1457
    !   2        Energy              
1458
    !   3        dEnergy/dt            
1459
    !   4        J_ant.E             
1460
    !   5        [h_(i+1)*h_*]/2 * C[h_(i+1)] * T_0 
1461
    !   6       -[h H(h) * T_0]
1462
    !   7       -[h C(h) * T_0]
1463
    !   8        [h w_* h]
1464
    !   9        [h * (q dchi/dt - dh/dt * T0)]_1
1465
    !  10      sum (h C(h) * T_0)  in total, as in 5, 6      
1466
    !  11     -sum (h H(h) * T_0)      
1467
    !  12     -sum (h C(h) * T_0)   
1468
    !  13      sum (h w_* h)  
1469
    !  14      sum [h (q dchi/dt - dh/dt * T0)]
1470
    !  15      3 + 4 + 9 + 10
1471
    !  16      (k_perp A)**2
1472
    !  17      B_par**2
1473
    !  18      df ** 2
1474
    !  19      h ** 2
1475
    !  20      Phi_bar ** 2
1476
    
1477
    write (unit=file_unit, fmt="(28es12.4)") t,h % energy,  &
×
1478
         h % energy_dot, h % antenna, h % imp_colls, h % hypercoll, h % collisions, &
×
1479
         h % gradients, h % heating, sum(h % imp_colls), sum(h % hypercoll), sum(h % collisions), &
×
1480
         sum(h % gradients), sum(h % heating),sum(h%heating)+h%antenna+sum(h%gradients)+h%energy_dot, &
×
1481
         h % eapar, h % ebpar, h % delfs2(:),  h % hs2(:), h % phis2(:)
×
1482

1483
    do is=1,nspec
×
1484
      write (unit=file_unit2, fmt="(15es12.4)") t,h % energy,  &
×
1485
           h % energy_dot, h % antenna, h % imp_colls(is), h % hypercoll(is), h % collisions(is), &
×
1486
           h % gradients(is), h % heating(is), &
×
1487
           h % eapar, h % ebpar, h % delfs2(is),  h % hs2(is), h % phis2(is), real(is)
×
1488
      write (unit=file_unit2, fmt=*)
×
1489
    end do
1490
    write (unit=file_unit2, fmt=*)
×
1491

1492
    flush (file_unit)
×
1493
    flush (file_unit2)
×
1494
  end subroutine do_write_heating
×
1495

1496
  !> Calculate the momentum flux as a function of \((v_\parallel, \theta, t)\)
1497
  !> and write to netCDF
1498
  subroutine do_write_symmetry(file_id, nout)
×
1499
    use diagnostics_fluxes, only: flux_vs_theta_vs_vpa
1500
    use theta_grid, only: ntgrid
1501
    use le_grids, only: nlambda, negrid
1502
    use species, only: nspec
1503
    use mp, only: proc0
1504
    use gs2_io, only: nc_loop_sym
1505
    use fields_arrays, only: phinew
1506
    implicit none
1507
    !> NetCDF ID of the file to write to
1508
    integer, intent(in) :: file_id
1509
    !> Current timestep
1510
    integer, intent(in) :: nout
1511

1512
    real, dimension(:,:,:), allocatable :: vflx_sym
×
1513
    allocate (vflx_sym(-ntgrid:ntgrid,nlambda*negrid,nspec))
×
1514
    call flux_vs_theta_vs_vpa (phinew, vflx_sym)
×
1515
    if (proc0) call nc_loop_sym (file_id, nout, vflx_sym)
×
1516
    deallocate (vflx_sym)
×
1517
  end subroutine do_write_symmetry
×
1518

1519
  subroutine do_write_kinetic_energy_transfer(file_id, nout)
×
1520
    use diagnostics_kinetic_energy_transfer, only: calculate_kinetic_energy_transfer, write_kinetic_energy_transfer
1521
    use mp, only: proc0
1522
    !> NetCDF ID of the file to write to
1523
    integer, intent(in) :: file_id
1524
    !> Current timestep
1525
    integer, intent(in) :: nout
1526
    call calculate_kinetic_energy_transfer
×
1527
    if (proc0) call write_kinetic_energy_transfer(file_id, nout)
×
1528
  end subroutine do_write_kinetic_energy_transfer
×
1529

1530
  !> Calculate the poloidal distributions of the fluxes of particles, parallel
1531
  !> momentum, perpendicular momentum, and energy (see section 3.1 and appendix
1532
  !> A of [Ball et al. PPCF 58 (2016)
1533
  !> 045023](https://doi.org/10.1088/0741-3335/58/4/045023) as well as section 5
1534
  !> of "GS2 analytic geometry specification")
1535
  subroutine do_write_nl_flux_dist(file_id, nout)
×
1536
    use diagnostics_fluxes, only: flux_dist
1537
    use theta_grid, only: ntgrid
1538
    use species, only: nspec, spec
1539
    use mp, only: proc0
1540
    use gs2_io, only: nc_loop_dist
1541
    use kt_grids, only: naky, ntheta0
1542
    use fields_arrays, only: phinew, bparnew
1543
    use run_parameters, only: has_phi
1544
    implicit none
1545
    !> NetCDF ID of the file to write to
1546
    integer, intent(in) :: file_id
1547
    !> Current timestep
1548
    integer, intent(in) :: nout
1549

1550
    real, dimension (:,:), allocatable :: part_fluxes_dist, mom_fluxes_par_dist
×
1551
    real, dimension (:,:), allocatable :: phi_dist, mom_fluxes_perp_dist, heat_fluxes_dist
×
1552
    real, dimension (:,:,:,:), allocatable :: pflux_dist, vflux_par_dist, vflux_perp_dist, qflux_dist
×
1553
    integer :: ig, is
1554
    if (.not. has_phi) return
×
1555

1556
    allocate (pflux_dist(-ntgrid:ntgrid,ntheta0,naky,nspec))
×
1557
    allocate (vflux_par_dist(-ntgrid:ntgrid,ntheta0,naky,nspec))
×
1558
    allocate (vflux_perp_dist(-ntgrid:ntgrid,ntheta0,naky,nspec))
×
1559
    allocate (qflux_dist(-ntgrid:ntgrid,ntheta0,naky,nspec))
×
1560

1561
    call flux_dist (phinew, bparnew, pflux_dist, vflux_par_dist, vflux_perp_dist, qflux_dist)
×
1562

1563
    if (proc0) then
×
1564
       allocate (part_fluxes_dist(-ntgrid:ntgrid,nspec))
×
1565
       allocate (mom_fluxes_par_dist(-ntgrid:ntgrid,nspec))
×
1566
       allocate (mom_fluxes_perp_dist(-ntgrid:ntgrid,nspec))
×
1567
       allocate (heat_fluxes_dist(-ntgrid:ntgrid,nspec))
×
1568
       allocate (phi_dist(2,-ntgrid:ntgrid))
×
1569
       do is = 1, nspec
×
1570
          do ig = -ntgrid, ntgrid
×
1571
             call get_volume_average (pflux_dist(ig,:,:,is), part_fluxes_dist(ig,is))
×
1572
             call get_volume_average (vflux_par_dist(ig,:,:,is), mom_fluxes_par_dist(ig,is))
×
1573
             call get_volume_average (vflux_perp_dist(ig,:,:,is), mom_fluxes_perp_dist(ig,is))
×
1574
             call get_volume_average (qflux_dist(ig,:,:,is), heat_fluxes_dist(ig,is))
×
1575
          end do
1576

1577
          part_fluxes_dist(:, is) = part_fluxes_dist(:, is) * spec(is)%dens
×
1578
          mom_fluxes_par_dist(:, is) = mom_fluxes_par_dist(:, is) * spec(is)%dens * sqrt(spec(is)%mass * spec(is)%temp)             
×
1579
          mom_fluxes_perp_dist(:, is) = mom_fluxes_perp_dist(:, is) * spec(is)%dens * sqrt(spec(is)%mass * spec(is)%temp)
×
1580
          heat_fluxes_dist(:, is) = heat_fluxes_dist(:, is) * spec(is)%dens * spec(is)%temp
×
1581
       end do
1582

1583
       do ig = -ntgrid, ntgrid
×
1584
          call get_volume_average (real(phinew(ig,:,:)), phi_dist(1,ig))
×
1585
          call get_volume_average (aimag(phinew(ig,:,:)), phi_dist(2,ig))
×
1586
       end do
1587

1588
       call nc_loop_dist (file_id, nout, part_fluxes_dist, mom_fluxes_par_dist, &
1589
            mom_fluxes_perp_dist, heat_fluxes_dist, phi_dist)
×
1590

1591
       deallocate (part_fluxes_dist, mom_fluxes_par_dist, mom_fluxes_perp_dist)
×
1592
       deallocate (heat_fluxes_dist, phi_dist)
×
1593
     end if
1594
    deallocate (pflux_dist, vflux_par_dist, vflux_perp_dist, qflux_dist)
×
1595
   end subroutine do_write_nl_flux_dist
×
1596

1597
  !> Calculate the correlation function over the physical domain and write to netCDF
1598
  subroutine do_write_correlation(file_id, nout)
×
1599
    use theta_grid, only: ntgrid
1600
    use kt_grids, only: naky
1601
    use mp, only: proc0
1602
    use gs2_io, only: nc_loop_corr
1603
    implicit none
1604
    !> NetCDF ID of the file to write to
1605
    integer, intent(in) :: file_id
1606
    !> Current timestep
1607
    integer, intent(in) :: nout
1608
    complex, dimension(:,:), allocatable :: phi_corr_2pi
×
1609
    allocate (phi_corr_2pi(-ntgrid:ntgrid,naky))
×
1610
    call correlation (phi_corr_2pi)
×
1611
    if (proc0) call nc_loop_corr (file_id, nout, phi_corr_2pi)
×
1612
    deallocate (phi_corr_2pi)
×
1613
  end subroutine do_write_correlation
×
1614

1615
  !> Calculate the correlation function over the extended domain and write to netCDF
1616
  !>
1617
  !> This does the calculation every time it is called, but only writes every
1618
  !> `nwrite_mult * nwrite` timesteps
1619
  subroutine do_write_correlation_extend(file_id, time, time_old)
×
1620
    use theta_grid, only: ntgrid
1621
    use kt_grids, only: jtwist, ntheta0, naky
1622
    use mp, only: proc0
1623
    use gs2_io, only: nc_loop_corr_extend
1624
    implicit none
1625
    !> NetCDF ID of the file to write to
1626
    integer, intent(in) :: file_id
1627
    !> Current and previous simulation times
1628
    real, intent(in) :: time, time_old
1629
    complex, dimension (:,:,:), allocatable, save:: phicorr_sum
1630
    real, dimension (:,:,:), allocatable, save :: phiextend_sum
1631
    complex, dimension (:,:,:), allocatable :: phi_corr
×
1632
    real, dimension (:,:,:), allocatable :: phi2_extend
×
1633
    real, save :: tcorr0 = 0.0
1634
    integer :: ntg_extend
1635
    if (.not. proc0) return
×
1636
    ntg_extend = (2*ntgrid+1) * ((ntheta0-1) / jtwist + 1)
×
1637

1638
    if (.not. allocated(phicorr_sum)) then
×
1639
       ! Warning: For ntheta=ntheta0=naky=128 and jtwist = 6 these two arrays
1640
       ! will require 750 MB and 375 MB respectively and they are not freed
1641
       ! after leaving the routine.
1642
       allocate (phicorr_sum(ntg_extend,ntheta0,naky)) ; phicorr_sum = 0.0
×
1643
       allocate (phiextend_sum(ntg_extend,ntheta0,naky)) ; phiextend_sum = 0.0
×
1644
       tcorr0 = time_old
×
1645
    end if
1646

1647
    allocate (phi_corr(ntg_extend,ntheta0,naky))
×
1648
    allocate (phi2_extend(ntg_extend,ntheta0,naky))
×
1649
    call correlation_extend (phi_corr, phi2_extend, ntg_extend)
×
1650
    phicorr_sum = phicorr_sum + phi_corr*(time-time_old)
×
1651
    phiextend_sum = phiextend_sum + phi2_extend*(time-time_old)
×
1652
    call nc_loop_corr_extend (file_id, nout_big, time, phicorr_sum/(time-tcorr0), &
×
1653
         phiextend_sum/(time-tcorr0))
×
1654
    nout_big = nout_big + 1
×
1655
    deallocate (phi_corr, phi2_extend)
×
1656
  end subroutine do_write_correlation_extend
×
1657

1658
  !> Calculate average parity of distribution function under \((\theta, k_x,
1659
  !> v_\parallel) \to (-\theta, -k_x, -v_\parallel)\), and write to [[parity_unit]]
1660
  subroutine do_write_parity(t, file_unit, write_text)
×
1661
    use theta_grid, only: ntgrid, field_line_average
×
1662
    use kt_grids, only: naky, ntheta0
1663
    use le_grids, only: negrid, nlambda, integrate_moment, integrate_kysum
1664
    use species, only: nspec
1665
    use gs2_layouts, only: init_parity_layouts, p_lo, g_lo, idx_local
1666
    use gs2_layouts, only: ik_idx, it_idx, il_idx, ie_idx, is_idx, idx, proc_id
1667
    use mp, only: send, receive, proc0, iproc, nproc
1668
    use dist_fn_arrays, only: gnew, g_adjust, aj0, to_g_gs2, from_g_gs2
1669
    use fields_arrays, only: phinew, bparnew
1670
    use constants, only: zi
1671
    implicit none
1672
    real, intent(in) :: t
1673
    !> Open formatted file to write text to
1674
    integer, intent(in) :: file_unit
1675
    !> If false, don't calculate or write parity
1676
    logical, intent(in) :: write_text
1677
    integer :: iplo, iglo, sgn2, isgn, il, ie, ig, ik, it, is, it_source
1678
    complex, dimension (:,:,:,:), allocatable :: gparity, gmx, gpx
×
1679
    complex, dimension (:,:,:), allocatable :: g0, gm, gp
×
1680
    complex, dimension (:,:,:), allocatable :: g_kint, gm_kint, gp_kint
×
1681
    complex, dimension (:,:), allocatable :: g_avg, gnorm_avg, phim
×
1682
    complex, dimension (:), allocatable :: g_all_tot, g_nokx_tot, g_nosig_tot, gtmp
×
1683
    complex, dimension (:), allocatable :: gnorm_all_tot, gnorm_nokx_tot, gnorm_nosig_tot
×
1684
    real, dimension (:,:,:), allocatable :: gmnorm, gmint, gpint, gmnormint, gmavg, gpavg, gmnormavg
×
1685
    real, dimension (:), allocatable :: gmtot, gptot, gtot, gmnormtot
×
1686
    real, dimension (:), allocatable :: gm_nokx_tot, gp_nokx_tot, gmnorm_nokx_tot
×
1687
    real, dimension (:), allocatable :: gm_nosig_tot, gp_nosig_tot, gmnorm_nosig_tot
×
1688

1689
    if (.not. write_text) return
×
1690

1691
    ! initialize layouts for parity diagnostic. Only does work on the first call
1692
    call init_parity_layouts (naky, nlambda, negrid, nspec, nproc, iproc)
×
1693

1694
    allocate (gparity(-ntgrid:ntgrid,ntheta0,2,p_lo%llim_proc:p_lo%ulim_alloc))
×
1695
    allocate (g0(-ntgrid:ntgrid,2,p_lo%llim_proc:p_lo%ulim_alloc))
×
1696
    allocate (gm(-ntgrid:ntgrid,2,p_lo%llim_proc:p_lo%ulim_alloc))
×
1697
    allocate (gp(-ntgrid:ntgrid,2,p_lo%llim_proc:p_lo%ulim_alloc))
×
1698
    allocate (gmnorm(-ntgrid:ntgrid,2,p_lo%llim_proc:p_lo%ulim_alloc))
×
1699
    allocate (gmint(-ntgrid:ntgrid,naky,nspec))
×
1700
    allocate (gpint(-ntgrid:ntgrid,naky,nspec))
×
1701
    allocate (gmnormint(-ntgrid:ntgrid,naky,nspec))
×
1702
    allocate (gmavg(ntheta0,naky,nspec))
×
1703
    allocate (gpavg(ntheta0,naky,nspec))
×
1704
    allocate (gmnormavg(ntheta0,naky,nspec))
×
1705
    allocate (gmtot(nspec), gm_nokx_tot(nspec), gm_nosig_tot(nspec))
×
1706
    allocate (gptot(nspec), gp_nokx_tot(nspec), gp_nosig_tot(nspec))
×
1707
    allocate (gtot(nspec), gmnormtot(nspec), gmnorm_nokx_tot(nspec), gmnorm_nosig_tot(nspec))
×
1708
    allocate (phim(-ntgrid:ntgrid,naky))
×
1709
    allocate (g_kint(-ntgrid:ntgrid,2,nspec), gm_kint(-ntgrid:ntgrid,2,nspec), gp_kint(-ntgrid:ntgrid,2,nspec))
×
1710
    allocate (g_avg(ntheta0,nspec), gnorm_avg(ntheta0,nspec))
×
1711
    allocate (g_all_tot(nspec), g_nokx_tot(nspec), g_nosig_tot(nspec), gtmp(nspec))
×
1712
    allocate (gnorm_all_tot(nspec), gnorm_nokx_tot(nspec), gnorm_nosig_tot(nspec))
×
1713

1714
    ! convert from g to h
1715
    call g_adjust (gnew, phinew, bparnew, direction = from_g_gs2)
×
1716

1717
    ! below we define gparity = J0 * h, where delta f = h - (q phi / T) F0
1718
    ! because we're ultimately interested in int J0 h * phi (i.e. particle flux)
1719
    ! This should probably be written as a redistribute gather/scatter?
1720
    do iglo = g_lo%llim_world, g_lo%ulim_world
×
1721
       ik = ik_idx(g_lo, iglo)
×
1722
       ie = ie_idx(g_lo, iglo)
×
1723
       il = il_idx(g_lo, iglo)
×
1724
       is = is_idx(g_lo, iglo)
×
1725
       it = it_idx(g_lo, iglo)
×
1726
       ! count total index for given (ik,il,ie,is) combo (dependent on layout)
1727
       iplo = idx(p_lo, ik, il, ie, is)
×
1728
       ! check to see if value of g corresponding to iglo is on this processor
1729
       if (idx_local(g_lo, iglo)) then
×
1730
          if (idx_local(p_lo, iplo)) then
×
1731
             ! if g value corresponding to iplo should be on this processor, then get it
1732
             gparity(:, it, :, iplo) = gnew(:, :, iglo) * spread(aj0(:, iglo), 2, 2)
×
1733
          else
1734
             ! otherwise, send g for this iplo value to correct processor
1735
             call send (gnew(:, :, iglo) * spread(aj0(:, iglo), 2, 2), proc_id(p_lo, iplo))
×
1736
          end if
1737
       else if (idx_local(p_lo, iplo)) then
×
1738
          ! if g for this iplo not on processor, receive it
1739
          call receive (gparity(:, it, :, iplo), proc_id(g_lo, iglo))
×
1740
       end if
1741
    end do
1742

1743
    ! convert from h back to g
1744
    call g_adjust (gnew, phinew, bparnew, direction = to_g_gs2)
×
1745

1746
    ! first diagnostic is phase space average of 
1747
    ! |J0*(h(z,vpa,kx) +/- h(-z,-vpa,-kx))|**2 / ( |J0*h(z,vpa,kx)|**2 + |J0*h(-z,-vpa,-kx)|**2 )
1748
    do it = 1, ntheta0
×
1749
       ! have to treat kx=0 specially
1750
       if (it == 1) then
×
1751
          it_source = 1
×
1752
       else
1753
          it_source = ntheta0 - it + 2
×
1754
       end if
1755

1756
       do isgn = 1, 2
×
1757
          sgn2 = mod(isgn,2)+1
×
1758
          do ig = -ntgrid, ntgrid
×
1759
             ! g0 = gparity(ntgrid:-ntgrid:-1, it_source, 2:1:-1, :) -- copy reversing theta and sigma
1760
             g0(ig, isgn, :) = gparity(-ig, it_source, sgn2, :)
×
1761
          end do
1762
       end do
1763

1764
       gm = gparity(:,it,:,:)-g0
×
1765
       gp = gparity(:,it,:,:)+g0
×
1766
       gmnorm = real(g0*conjg(g0))
×
1767
       ! integrate out velocity dependence
1768
       call integrate_moment (real(gm*conjg(gm)),gmint,1)
×
1769
       call integrate_moment (real(gp*conjg(gp)),gpint,1)
×
1770
       call integrate_moment (gmnorm,gmnormint,1)
×
1771
       ! average along field line
1772
       gmavg(it,:,:) = field_line_average(gmint)
×
1773
       gpavg(it,:,:) = field_line_average(gpint)
×
1774
       gmnormavg(it,:,:) = field_line_average(gmnormint)
×
1775

1776
       ! phim(theta,kx) = phi(-theta,-kx)
1777
       do ig = -ntgrid, ntgrid
×
1778
          phim(ig,:) = phinew(-ig, it_source, :)
×
1779
       end do
1780

1781
       ! want int dtheta sum_{kx} int d3v | sum_{ky} [ J0*(h+ * conjg(phi-) + h- * conjg(phi+)) ky ] |**2
1782
       ! J0*(h+ * conjg(phi-) + h- * conjg(phi+)) = h*conjg(phi) - h(-theta,-vpa,-kx)*conjg(phi(-theta,-kx))
1783
       do iplo = p_lo%llim_proc, p_lo%ulim_proc
×
1784
          ik = ik_idx(p_lo,iplo)
×
1785
          do isgn = 1, 2
×
1786
             gm(:,isgn,iplo) = gparity(:,it,isgn,iplo)*conjg(phinew(:,it,ik)) - g0(:,isgn,iplo)*conjg(phim(:,ik))
×
1787
          end do
1788
       end do
1789
       call integrate_kysum (gm, g_kint, 1)
×
1790
       g_avg(it, :) = field_line_average(g_kint(:,1,:) + g_kint(:,2,:))
×
1791

1792
       ! get normalizing term for above diagnostic
1793
       do iplo = p_lo%llim_proc, p_lo%ulim_proc
×
1794
          ik = ik_idx(p_lo,iplo)
×
1795
          do isgn = 1, 2
×
1796
             gm(:,isgn,iplo) = gparity(:,it,isgn,iplo)*conjg(phinew(:,it,ik))
×
1797
             gp(:,isgn,iplo) = g0(:,isgn,iplo)*conjg(phim(:,ik))
×
1798
          end do
1799
       end do
1800
       call integrate_kysum (gm, gm_kint, 1)
×
1801
       call integrate_kysum (gp, gp_kint, 1)
×
1802
       gnorm_avg(it,:) = field_line_average(gm_kint(:,1,:)+gm_kint(:,2,:) &
×
1803
            + gp_kint(:,1,:)+gp_kint(:,2,:))
×
1804
    end do
1805

1806
    ! average over perp plane
1807
    do is = 1, nspec
×
1808
       call get_volume_average (gmavg(:,:,is), gmtot(is))
×
1809
       call get_volume_average (gpavg(:,:,is), gptot(is))
×
1810
       call get_volume_average (gmnormavg(:,:,is), gmnormtot(is))    
×
1811
       g_all_tot(is) = sum(g_avg(:,is))
×
1812
       gnorm_all_tot(is) = sum(gnorm_avg(:,is))
×
1813
    end do
1814

1815
    allocate (gmx(-ntgrid:ntgrid,ntheta0,2,p_lo%llim_proc:p_lo%ulim_alloc))
×
1816
    allocate (gpx(-ntgrid:ntgrid,ntheta0,2,p_lo%llim_proc:p_lo%ulim_alloc))
×
1817

1818
    ! now we want diagnostic of phase space average of
1819
    ! |J0*(h(z,vpa) +/- h(-z,-vpa))|**2 / ( |J0*h(z,vpa)|**2 + |J0*h(-z,-vpa)|**2 )
1820
    do it = 1, ntheta0
×
1821
       do isgn = 1, 2
×
1822
          sgn2 = mod(isgn,2)+1
×
1823
          do ig = -ntgrid, ntgrid
×
1824
             g0(ig,isgn,:) = gparity(-ig,it,sgn2,:)
×
1825
          end do
1826
       end do
1827
       gm = gparity(:,it,:,:)-g0
×
1828
       gp = gparity(:,it,:,:)+g0
×
1829
       gmnorm = real(g0*conjg(g0))
×
1830
       ! integrate out velocity dependence
1831
       call integrate_moment (real(gm*conjg(gm)),gmint,1)
×
1832
       call integrate_moment (real(gp*conjg(gp)),gpint,1)
×
1833
       call integrate_moment (gmnorm,gmnormint,1)
×
1834
       ! average along field line
1835
       gmavg(it,:,:) = field_line_average(gmint)
×
1836
       gpavg(it,:,:) = field_line_average(gpint)
×
1837
       gmnormavg(it,:,:) = field_line_average(gmnormint)
×
1838

1839
       ! phim(theta) = phi(-theta)
1840
       ! have to treat kx=0 specially
1841
       do ig = -ntgrid, ntgrid
×
1842
          phim(ig,:) = phinew(-ig,it,:)
×
1843
       end do
1844

1845
       ! want int dtheta int d3v | sum_{kx} sum_{ky} [ J0*(h+ * conjg(phi-) + h- * conjg(phi+)) ky ] |**2
1846
       ! J0*(h+ * conjg(phi-) + h- * conjg(phi+)) = h*conjg(phi) - h(-theta,-vpa)*conjg(phi(-theta))
1847
       do iplo = p_lo%llim_proc, p_lo%ulim_proc
×
1848
          ik = ik_idx(p_lo,iplo)
×
1849
          do isgn = 1, 2
×
1850
             gmx(:,it,isgn,iplo) = g0(:,isgn,iplo)*conjg(phim(:,ik))
×
1851
             gpx(:,it,isgn,iplo) = gparity(:,it,isgn,iplo)*conjg(phinew(:,it,ik)) - gmx(:,it,isgn,iplo) 
×
1852
          end do
1853
       end do
1854
    end do
1855

1856
    ! sum over kx
1857
    gp = sum(gpx,2)
×
1858
    deallocate (gpx)
×
1859
    call integrate_kysum(gp, g_kint, 1)
×
1860
    g_nokx_tot = field_line_average(g_kint(:,1,:) + g_kint(:,2,:))
×
1861

1862
    ! get normalizing terms for above diagnostic
1863
    gm = sum(gmx,2)
×
1864
    deallocate (gmx)
×
1865
    call integrate_kysum(gm, gm_kint, 1)
×
1866
    call integrate_kysum(gm + gp, gp_kint, 1)
×
1867
    gnorm_nokx_tot = field_line_average(gm_kint(:,1,:)+gm_kint(:,2,:) &
×
1868
         + gp_kint(:,1,:)+gp_kint(:,2,:))
×
1869

1870
    ! average over perp plane
1871
    do is = 1, nspec
×
1872
       call get_volume_average (gmavg(:,:,is), gm_nokx_tot(is))
×
1873
       call get_volume_average (gpavg(:,:,is), gp_nokx_tot(is))
×
1874
       call get_volume_average (gmnormavg(:,:,is), gmnorm_nokx_tot(is))    
×
1875
    end do
1876

1877
    ! final diagnostic is phase space average of 
1878
    ! |J0*(h(z,kx) +/- h(-z,-kx))|**2 / ( |J0*h(z,kx)|**2 + |J0*h(-z,-kx)|**2 )
1879
    do it = 1, ntheta0
×
1880
       ! have to treat kx=0 specially
1881
       if (it == 1) then
×
1882
          it_source = 1
×
1883
       else
1884
          it_source = ntheta0 - it + 2
×
1885
       end if
1886

1887
       do ig = -ntgrid, ntgrid
×
1888
          g0(ig, :, :) = gparity(-ig, it_source, :, :)
×
1889
       end do
1890

1891
       gm = gparity(:,it,:,:)-g0
×
1892
       gp = gparity(:,it,:,:)+g0
×
1893
       gmnorm = real(g0*conjg(g0))
×
1894
       ! integrate out velocity dependence
1895
       call integrate_moment (real(gm*conjg(gm)),gmint,1)
×
1896
       call integrate_moment (real(gp*conjg(gp)),gpint,1)
×
1897
       call integrate_moment (gmnorm,gmnormint,1)
×
1898
       ! average along field line
1899
       gmavg(it,:,:) = field_line_average(gmint)
×
1900
       gpavg(it,:,:) = field_line_average(gpint)
×
1901
       gmnormavg(it,:,:) = field_line_average(gmnormint)
×
1902

1903
       ! phim(theta,kx) = phi(-theta,-kx)
1904
       do ig = -ntgrid, ntgrid
×
1905
          phim(ig, :) = phinew(-ig, it_source, :)
×
1906
       end do
1907

1908
       ! want int dtheta sum_{kx} int dE dmu | sum_{sigma} sum_{ky} [ J0*(h+ * conjg(phi-) + h- * conjg(phi+)) ky ] |**2
1909
       ! J0*(h+ * conjg(phi-) + h- * conjg(phi+)) = h*conjg(phi) - h(-theta,-kx)*conjg(phi(-theta,-kx))
1910
       do iplo = p_lo%llim_proc, p_lo%ulim_proc
×
1911
          ik = ik_idx(p_lo,iplo)
×
1912
          do isgn = 1, 2
×
1913
             gm(:,isgn,iplo) = gparity(:,it,isgn,iplo)*conjg(phinew(:,it,ik)) - g0(:,isgn,iplo)*conjg(phim(:,ik))
×
1914
          end do
1915
       end do
1916

1917
       ! Only sets isgn = 1 in the g_kint array here
1918
       call integrate_kysum (spread(gm(:, 1, :) + gm(:, 2, :), 2, 1), g_kint, 1)
×
1919
       g_avg(it, :) = field_line_average(g_kint(:,1,:))
×
1920

1921
       ! get normalizing term for above diagnostic
1922
       do iplo = p_lo%llim_proc, p_lo%ulim_proc
×
1923
          ik = ik_idx(p_lo,iplo)
×
1924
          do isgn = 1, 2
×
1925
             gm(:,isgn,iplo) = gparity(:,it,isgn,iplo)*conjg(phinew(:,it,ik))
×
1926
             gp(:,isgn,iplo) = g0(:,isgn,iplo)*conjg(phim(:,ik))
×
1927
          end do
1928
       end do
1929

1930
       ! Only sets isgn = 1 in the g*_kint arrays here
1931
       call integrate_kysum (spread(gm(:, 1, :) + gm(:, 2, :), 2, 1), gm_kint, 1)
×
1932
       call integrate_kysum (spread(gp(:, 1, :) + gp(:, 2, :), 2, 1), gp_kint, 1)
×
1933
       gnorm_avg(it, :) = field_line_average(gm_kint(:,1,:) + gp_kint(:,1,:))
×
1934
    end do
1935

1936
    ! average over perp plane
1937
    do is = 1, nspec
×
1938
       call get_volume_average (gmavg(:,:,is), gm_nosig_tot(is))
×
1939
       call get_volume_average (gpavg(:,:,is), gp_nosig_tot(is))
×
1940
       call get_volume_average (gmnormavg(:,:,is), gmnorm_nosig_tot(is))    
×
1941
       g_nosig_tot(is) = sum(g_avg(:,is))
×
1942
       gnorm_nosig_tot(is) = sum(gnorm_avg(:,is))
×
1943
    end do
1944

1945
    deallocate (gparity) ; allocate (gparity(-ntgrid:ntgrid,ntheta0,naky,nspec))
×
1946
    ! obtain normalization factor = int over phase space of |g|**2
1947
    call g_adjust (gnew, phinew, bparnew, direction = from_g_gs2)
×
1948
    !Do all processors need to know the full result here? Only proc0 seems to do any writing.
1949
    !If not then remove the last two arguments in the following call.
1950
    call integrate_moment (spread(aj0,2,2)*spread(aj0,2,2)*gnew*conjg(gnew), gparity, .true., full_arr=.true.)
×
1951
    call g_adjust (gnew, phinew, bparnew, direction = to_g_gs2)
×
1952
    do is = 1, nspec
×
1953
       gmavg(:, :, is) = field_line_average(real(gparity(:,:,:,is)))
×
1954
       call get_volume_average (gmavg(:,:,is), gtot(is))
×
1955
    end do
1956

1957
    ! normalize g(theta,vpa,kx) - g(-theta,-vpa,-kx) terms
1958
    where (gtot+gmnormtot > epsilon(0.0))
×
1959
       gmtot = sqrt(gmtot/(gtot+gmnormtot)) ; gptot = sqrt(gptot/(gtot+gmnormtot))
×
1960
    elsewhere
1961
       gmtot = sqrt(gmtot) ; gptot = sqrt(gptot)
×
1962
    end where
1963

1964
    where (real(gnorm_all_tot) > epsilon(0.0))
×
1965
       gtmp = sqrt(real(g_all_tot)/real(gnorm_all_tot))
×
1966
    elsewhere
1967
       gtmp = sqrt(real(g_all_tot))
×
1968
    end where
1969
    where (aimag(gnorm_all_tot) > epsilon(0.0))
×
1970
       g_all_tot = gtmp + zi*sqrt(aimag(g_all_tot)/aimag(gnorm_all_tot))
×
1971
    elsewhere
1972
       g_all_tot = gtmp + zi*sqrt(aimag(g_all_tot))
×
1973
    end where
1974

1975
    ! normalize g(theta,vpa) +/- g(-theta,-vpa) terms
1976
    where (gtot+gmnorm_nokx_tot > epsilon(0.0))
×
1977
       gm_nokx_tot = sqrt(gm_nokx_tot/(gtot+gmnorm_nokx_tot)) ; gp_nokx_tot = sqrt(gp_nokx_tot/(gtot+gmnorm_nokx_tot))
×
1978
    elsewhere
1979
       gm_nokx_tot = sqrt(gm_nokx_tot) ; gp_nokx_tot = sqrt(gp_nokx_tot)
×
1980
    end where
1981

1982
    where (real(gnorm_nokx_tot) > epsilon(0.0))
×
1983
       gtmp = sqrt(real(g_nokx_tot)/real(gnorm_nokx_tot))
×
1984
    elsewhere
1985
       gtmp = sqrt(real(g_nokx_tot))
×
1986
    end where
1987
    where (aimag(gnorm_nokx_tot) > epsilon(0.0))
×
1988
       g_nokx_tot = gtmp + zi*sqrt(aimag(g_nokx_tot)/aimag(gnorm_nokx_tot))
×
1989
    elsewhere
1990
       g_nokx_tot = gtmp + zi*sqrt(aimag(g_nokx_tot))
×
1991
    end where
1992

1993
    ! normalize g(theta,kx) +/ g(-theta,-kx) terms
1994
    where (gtot+gmnorm_nosig_tot > epsilon(0.0))
×
1995
       gm_nosig_tot = sqrt(gm_nosig_tot/(gtot+gmnorm_nosig_tot)) ; gp_nosig_tot = sqrt(gp_nosig_tot/(gtot+gmnorm_nosig_tot))
×
1996
    elsewhere
1997
       gm_nosig_tot = sqrt(gm_nosig_tot) ; gp_nosig_tot = sqrt(gp_nosig_tot)
×
1998
    end where
1999

2000
    where (real(gnorm_nosig_tot) > epsilon(0.0))
×
2001
       gtmp = sqrt(real(g_nosig_tot)/real(gnorm_nosig_tot))
×
2002
    elsewhere
2003
       gtmp = sqrt(real(g_nosig_tot))
×
2004
    end where
2005
    where (aimag(gnorm_nosig_tot) > epsilon(0.0))
×
2006
       g_nosig_tot = gtmp + zi*sqrt(aimag(g_nosig_tot)/aimag(gnorm_nosig_tot))
×
2007
    elsewhere
2008
       g_nosig_tot = gtmp + zi*sqrt(aimag(g_nosig_tot))
×
2009
    end where
2010

2011
    if (proc0) write (file_unit,"(19(1x,e12.5))") t, gmtot, gptot, real(g_all_tot), aimag(g_all_tot), &
×
2012
         real(gnorm_all_tot), aimag(gnorm_all_tot), gm_nokx_tot, gp_nokx_tot, real(g_nokx_tot), aimag(g_nokx_tot), &
×
2013
         real(gnorm_nokx_tot), aimag(gnorm_nokx_tot), gm_nosig_tot, gp_nosig_tot, &
×
2014
         real(g_nosig_tot), aimag(g_nosig_tot), real(gnorm_nosig_tot), aimag(gnorm_nosig_tot)
×
2015

2016
    deallocate (gparity, g0)
×
2017
    deallocate (gm, gp, gmnorm, gmint, gpint, gmnormint)
×
2018
    deallocate (gmavg, gpavg, gmnormavg)
×
2019
    deallocate (gmtot, gm_nokx_tot, gm_nosig_tot)
×
2020
    deallocate (gptot, gp_nokx_tot, gp_nosig_tot)
×
2021
    deallocate (gtot, gmnormtot, gmnorm_nokx_tot, gmnorm_nosig_tot)
×
2022
    deallocate (g_avg, gnorm_avg)
×
2023
    deallocate (g_kint, gm_kint, gp_kint)
×
2024
    deallocate (g_all_tot, g_nokx_tot, g_nosig_tot, gtmp)
×
2025
    deallocate (gnorm_all_tot, gnorm_nokx_tot, gnorm_nosig_tot)
×
2026
    deallocate (phim)
×
2027
  end subroutine do_write_parity
×
2028

2029
  !> Calculate flux surfgace averaged low-order moments of \(g\) and write to netCDF
2030
  subroutine do_write_avg_moments
×
2031
    use diagnostics_moments, only: getmoms
2032
    use mp, only: proc0
2033
    use kt_grids, only: ntheta0, naky
2034
    use species, only: nspec
2035
    use fields_arrays, only: phinew, bparnew
2036
    use gs2_io, only: nc_loop_moments
2037
    use theta_grid, only: ntgrid, field_line_average
2038
    use volume_averages, only: average_theta, average_all
2039
    implicit none
2040
    complex, dimension (-ntgrid:ntgrid,ntheta0,naky,nspec) :: ntot, density, &
×
2041
         upar, tpar, tperp, qparflux, pperpj1, qpperpj1
×
2042
    complex, dimension (ntheta0, nspec) :: ntot00, density00, upar00, tpar00, tperp00
×
2043
    complex, dimension (ntheta0) :: phi00
×
2044
    real, dimension (nspec) :: ntot2, ntot20, tpar2, tperp2
×
2045
    real, dimension (ntheta0, naky, nspec) :: ntot2_by_mode, ntot20_by_mode
×
2046
    real, dimension (ntheta0, naky, nspec) :: tpar2_by_mode, tperp2_by_mode
×
2047

2048
    ! We seem to call this routine a lot in one loop
2049
    call getmoms (phinew, bparnew, ntot, density, upar, tpar, tperp, qparflux, pperpj1, qpperpj1)
×
2050

2051
    if (proc0) then
×
2052
       ntot00 = field_line_average(ntot(:,:,1,:))
×
2053
       density00 = field_line_average(density(:,:,1,:))
×
2054
       upar00 = field_line_average(upar(:,:,1,:))
×
2055
       tpar00 = field_line_average(tpar(:,:,1,:))
×
2056
       tperp00 = field_line_average(tperp(:,:,1,:))
×
2057
       phi00 = field_line_average(phinew(:,:,1))
×
2058

2059
       call average_theta(ntot, ntot, ntot2_by_mode)
×
2060
       call average_all(ntot2_by_mode, ntot2)
×
2061
       call average_theta(tpar, tpar, tpar2_by_mode)
×
2062
       call average_all(tpar2_by_mode, tpar2)
×
2063
       call average_theta(tperp, tperp, tperp2_by_mode)
×
2064
       call average_all(tperp2_by_mode, tperp2)
×
2065

2066
       ntot20_by_mode = real(conjg(ntot(0, :, :, :)) * ntot(0, :, :, :))
×
2067
       call average_all(ntot20_by_mode, ntot20)
×
2068

2069
       call nc_loop_moments (get_netcdf_file_id(), nout, ntot2, ntot2_by_mode, ntot20, &
×
2070
            ntot20_by_mode, phi00, ntot00, density00, upar00, &
×
2071
            tpar00, tperp00, tpar2_by_mode, tperp2_by_mode)
×
2072
    end if
2073
  end subroutine do_write_avg_moments
×
2074

2075
  !> Calculate moments (density, parallel flow, and parallel and perpendicular
2076
  !> temperatures) in non-guiding centre coordinates and write to netCDF
2077
  subroutine do_write_full_moments_notgc()
×
2078
    use diagnostics_moments, only: getmoms_notgc
2079
    use theta_grid, only: ntgrid
2080
    use kt_grids, only: naky, ntheta0
2081
    use species, only: nspec
2082
    use mp, only: proc0
2083
    use gs2_io, only: nc_loop_fullmom
2084
    use fields_arrays, only: phinew, bparnew
2085
    implicit none
2086
    complex, dimension (-ntgrid:ntgrid,ntheta0,naky,nspec) :: ntot, density, &
×
2087
         upar, tpar, tperp
×
2088
    call getmoms_notgc(phinew, bparnew, density,upar,tpar,tperp,ntot)
×
2089
    if(proc0) then
×
2090
       call nc_loop_fullmom(get_netcdf_file_id(), nout, &
2091
            & ntot(igomega,:,:,:),density(igomega,:,:,:), &
×
2092
            & upar(igomega,:,:,:), &
×
2093
            & tpar(igomega,:,:,:),tperp(igomega,:,:,:) )
×
2094
    endif
2095
  end subroutine do_write_full_moments_notgc
×
2096

2097
  !> Flush text files (only [[out_unit]], [[res_unit]], [[lpc_unit]], and
2098
  !> [[parity_unit]])
2099
  subroutine flush_files
×
2100
    implicit none
2101
    if (.not. write_ascii) return
×
2102
    flush (out_unit)
×
2103
    if (write_verr) then
×
2104
       flush (res_unit)
×
2105
       flush (lpc_unit)
×
2106
    end if
2107
    if (write_cerr) flush (cres_unit)
×
2108
    if (write_parity) flush (parity_unit)
×
2109
    if (write_g) flush (dist_unit)
×
2110
    if (write_gyx) flush (yxdist_unit)
×
2111
  end subroutine flush_files
2112

2113
  !> Nonlinear convergence condition - simple and experimental look
2114
  !> for the averaged differential of the summed averaged heat flux to
2115
  !> drop below a threshold
2116
  subroutine check_nonlin_convergence(istep, heat_flux, exit)
×
2117
    use diagnostics_nonlinear_convergence, only: check_nonlin_convergence_calc
2118
    logical, intent(inout) :: exit
2119
    integer, intent(in) :: istep
2120
    real, intent(in) :: heat_flux
2121
    call check_nonlin_convergence_calc(istep, nwrite, heat_flux, exit, exit_when_converged, &
2122
         conv_nstep_av, conv_nsteps_converged, conv_test_multiplier, conv_min_step, &
2123
         conv_max_step)
×
2124
  end subroutine check_nonlin_convergence
×
2125

2126
  !> Calculate some heating diagnostics, and update their time history
2127
  subroutine heating(istep, navg, h, hk, h_hist, hk_hist)
×
2128
    use mp, only: proc0
2129
    use fields_arrays, only: phi, apar, bpar, phinew, aparnew, bparnew
2130
    use species, only: nspec, spec
2131
    use kt_grids, only: naky, ntheta0, aky, akx
2132
    use theta_grid, only: ntgrid, delthet, jacob
2133
    use gs2_heating, only: heating_diagnostics, avg_h, avg_hk, zero_htype, get_heat
2134
    use collisions, only: collisions_heating => heating, c_rate
2135
    implicit none
2136
    integer, intent (in) :: istep, navg
2137
    !> Heating diagnostics summed over space at the current timestep
2138
    type (heating_diagnostics), intent(in out) :: h
2139
    type (heating_diagnostics), dimension (0:), intent(in out) :: h_hist
2140
    !> Heating diagnostics as a function of \((\theta, k_y)\) at the current timestep
2141
    type (heating_diagnostics), dimension(:,:), intent(in out) :: hk
2142
    type (heating_diagnostics), dimension(:,:,0:), intent(in out) :: hk_hist
2143
    real, dimension(-ntgrid:ntgrid) :: wgt
×
2144
    real :: fac
2145
    integer :: is, ik, it, ig
2146

2147
    !Zero out variables for heating diagnostics
2148
    call zero_htype(h)
×
2149
    call zero_htype(hk)
×
2150

2151
    if (proc0 .and. collisions_heating .and. allocated(c_rate)) then
×
2152
       !GGH NOTE: Here wgt is 1/(2*ntgrid+1)
2153
       wgt = delthet*jacob
×
2154
       wgt = wgt/sum(wgt)
×
2155

2156
       do is = 1, nspec
×
2157
          do ik = 1, naky
×
2158
             fac = 0.5
×
2159
             if (aky(ik) < epsilon(0.)) fac = 1.0
×
2160
             do it = 1, ntheta0
×
2161
                if (aky(ik) < epsilon(0.0) .and. abs(akx(it)) < epsilon(0.0)) cycle
×
2162
                do ig = -ntgrid, ntgrid
×
2163

2164
                   !Sum heating by k over all z points (ig)
2165
                   hk(it, ik) % collisions(is) = hk(it, ik) % collisions(is) &
×
2166
                        + real(c_rate(ig,it,ik,is,1))*fac*wgt(ig)*spec(is)%temp*spec(is)%dens
×
2167

2168
                   hk(it, ik) % hypercoll(is) = hk(it, ik) % hypercoll(is) &
×
2169
                        + real(c_rate(ig,it,ik,is,2))*fac*wgt(ig)*spec(is)%temp*spec(is)%dens
×
2170

2171
                   hk(it, ik) % imp_colls(is) = hk(it, ik) % imp_colls(is) &
×
2172
                        + real(c_rate(ig,it,ik,is,3))*fac*wgt(ig)*spec(is)%temp*spec(is)%dens
×
2173

2174
                end do
2175
                h % collisions(is) = h % collisions(is) + hk(it, ik) % collisions(is)
×
2176
                h % hypercoll(is)  = h % hypercoll(is)  + hk(it, ik) % hypercoll(is)
×
2177
                h % imp_colls(is)  = h % imp_colls(is)  + hk(it, ik) % imp_colls(is)
×
2178
             end do
2179
          end do
2180
       end do
2181
    end if
2182

2183
    ! We may be able to skip this work as well if heating is false.
2184
    call get_heat (h, hk, phi, apar, bpar, phinew, aparnew, bparnew)
×
2185

2186
    call avg_h(h, h_hist, istep, navg)
×
2187
    call avg_hk(hk, hk_hist, istep, navg)
×
2188
  end subroutine heating
×
2189

2190
  !> Calculate the time-averaged antenna current \(j_{ext} = k_\perp^2 A_{antenna}\)
2191
  subroutine calc_jext (istep, j_ext)
×
2192
    use mp, only: proc0
2193
    use antenna, only: get_jext
2194
    implicit none
2195
    !> Current simulation timestep
2196
    integer, intent (in) :: istep
2197
    !Shouldn't really need intent in here but it's beacuse we zero it before calling calc_jext
2198
    real, dimension(:,:), intent(in out) ::  j_ext
2199

2200
    integer :: i
2201
    if (.not. (proc0 .and. navg > 1)) return
×
2202
    call get_jext(j_ext)
×
2203
    ! This looks a little odd as we ignore the first step
2204
    if (istep > 1) j_ext_hist(:, :, mod(istep, navg)) = j_ext(:, :)
×
2205

2206
    !Use average of history
2207
    if (istep >= navg) then
×
2208
       j_ext = 0.
×
2209
       do i = 0, navg - 1
×
2210
          j_ext = j_ext + j_ext_hist(:, :, i) / real(navg)
×
2211
       end do
2212
    end if
2213
  end subroutine calc_jext
2214

2215
  !> A linear estimate of the diffusivity, primarily used testing
2216
  pure real function diffusivity()
×
2217
    use kt_grids, only: ntheta0, naky, kperp2
2218
    use theta_grid, only: grho
2219
    use warning_helpers, only: is_zero
2220
    real, dimension(ntheta0, naky) :: diffusivity_by_k
×
2221
    integer  :: ik, it
2222
    diffusivity_by_k = 0.0
×
2223
    do ik = 1, naky
×
2224
       do it = 1, ntheta0
×
2225
          if (is_zero(kperp2(igomega,it,ik))) cycle
×
2226
          diffusivity_by_k(it,ik) = 2.0 &
×
2227
               * max(aimag(omegaavg(it,ik)), 0.0) / kperp2(igomega, it, ik)
×
2228
       end do
2229
    end do
2230
    diffusivity = maxval(diffusivity_by_k) * grho(igomega)
×
2231
  end function diffusivity
×
2232

2233
  !> Calculates <|field|**2 kperp2>_theta / <|field|**2>_theta.
2234
  !> Useful for simple quasilinear metric
2235
  pure function get_field_weighted_average_kperp2(field) result(average)
×
2236
    use kt_grids, only: kperp2, naky, ntheta0
2237
    use theta_grid, only: ntgrid, field_line_average
2238
    implicit none
2239
    complex, dimension(-ntgrid:ntgrid, ntheta0, naky), intent(in) :: field
2240
    real, dimension(ntheta0, naky) :: average
2241
    real, dimension(-ntgrid:ntgrid, ntheta0, naky) :: field_squared
×
2242
    integer :: it, ik
2243
    field_squared = abs(field) * abs(field)
×
2244
    average = 0.0
×
2245
    do ik = 1, naky
×
2246
       do it = 1, ntheta0
×
2247
          if (maxval(abs(field_squared(:, it, ik))) <= epsilon(0.0)) cycle
×
2248
          average(it, ik) = field_line_average(field_squared(:, it, ik) * &
×
2249
               kperp2(:, it, ik)) / field_line_average(field_squared(:, it, ik))
×
2250
       end do
2251
    end do
2252
  end function get_field_weighted_average_kperp2
×
2253

2254
  !> Calculates a simple gamma/kperp2 QL flux metric
2255
  function calculate_simple_quasilinear_flux_metric_by_k(growth_rates) result(ql_metric)
×
2256
    use run_parameters, only: has_phi, has_apar, has_bpar
2257
    use kt_grids, only: naky, ntheta0, aky
2258
    use fields_arrays, only: phinew, aparnew, bparnew
2259
    use warning_helpers, only: is_zero
2260
    implicit none
2261
    real, dimension(ntheta0, naky), intent(in) :: growth_rates
2262
    real, dimension(ntheta0, naky) :: limited_growth_rates, average
×
2263
    real, dimension(ntheta0, naky) :: ql_metric, normalising_field
×
2264
    integer :: ik
2265

2266
    ! Initialise flux to zero
2267
    ql_metric = 0.0
×
2268

2269
    ! Decide on the normalising field
2270
    if (has_phi) then
×
2271
       normalising_field = maxval(abs(phinew), dim = 1)
×
2272
    else if (has_apar) then
×
2273
       normalising_field = maxval(abs(aparnew), dim = 1)
×
2274
    else if (has_bpar) then
×
2275
       normalising_field = maxval(abs(bparnew), dim = 1)
×
2276
    else
2277
       ! If we get here then no fields are active so the QL flux
2278
       ! is zero and we can exit
2279
       return
×
2280
    end if
2281

2282
    where (normalising_field > 0)
×
2283
       normalising_field = 1.0 / normalising_field
×
2284
    end where
2285

2286
    if (has_phi) then
×
2287
       average = get_field_weighted_average_kperp2(phinew)
×
2288
       where(average > 0.0)
×
2289
          ql_metric = maxval(abs(phinew), dim = 1) * normalising_field / average
×
2290
       end where
2291
    end if
2292

2293
    if (has_apar) then
×
2294
       average = get_field_weighted_average_kperp2(aparnew)
×
2295
       where(average > 0.0)
×
2296
          ql_metric = ql_metric + &
×
2297
               maxval(abs(aparnew), dim = 1) * normalising_field / average
×
2298
       end where
2299
    end if
2300

2301
    if (has_bpar) then
×
2302
       average = get_field_weighted_average_kperp2(bparnew)
×
2303
       where(average > 0.0)
×
2304
          ql_metric = ql_metric + &
×
2305
               maxval(abs(bparnew), dim = 1) * normalising_field / average
×
2306
       end where
2307
    end if
2308

2309
    ! Limit the growth rate to positive values
2310
    where (growth_rates > 0)
×
2311
       limited_growth_rates = growth_rates
×
2312
    elsewhere
2313
       limited_growth_rates = 0.0
×
2314
    end where
2315

2316
    ! MG: Avoid spurious contribution from the zonal mode
2317
    do ik = 1, naky
×
2318
       if (is_zero(aky(ik))) ql_metric(:, ik) = 0.0
×
2319
    end do
2320

2321
    ql_metric = ql_metric * limited_growth_rates
×
2322
  end function calculate_simple_quasilinear_flux_metric_by_k
×
2323

2324
  !> Calculates the instantaneous omega from chinew = chi * exp(-i * omega * dt)
2325
  !> with chi = phi + apar + bpar. This gives omega = log(chinew/chi) * i / dt.
2326
  pure function  calculate_instantaneous_omega(ig, tolerance) result(omega_inst)
×
2327
    use kt_grids, only: naky, ntheta0
2328
    use fields_arrays, only: phi, apar, bpar, phinew, aparnew, bparnew
2329
    use gs2_time, only: code_dt
2330
    use constants, only: zi
2331
    use optionals, only: get_option_with_default
2332
    use run_parameters, only: has_phi, has_apar, has_bpar
2333
    use warning_helpers, only: is_zero
2334
    implicit none
2335
    integer, intent(in), optional :: ig
2336
    real, intent(in), optional :: tolerance
2337
    complex, dimension(ntheta0, naky) :: omega_inst, field_sum, field_sum_new
×
2338
    integer :: ig_use
2339
    real :: tol_use, too_small
2340

2341
    ! Determine which theta grid point to evaluate the fields at
2342
    ig_use = get_option_with_default(ig, igomega)
×
2343

2344
    ! Construct the field sums for current and old fields.
2345
    ! We always allocate the fields (for now) so we could just
2346
    ! unconditionally sum. This could make the code clearer
2347
    ! without really adding _much_ overhead.
2348
    if (has_phi) then
×
2349
       field_sum = phi(ig_use, :, :)
×
2350
       field_sum_new = phinew(ig_use, :, :)
×
2351
    else
2352
       field_sum = 0.0
×
2353
       field_sum_new = 0.0
×
2354
    end if
2355

2356
    if (has_apar) then
×
2357
       field_sum = field_sum + apar(ig_use, :, :)
×
2358
       field_sum_new = field_sum_new + aparnew(ig_use, :, :)
×
2359
    end if
2360

2361
    if (has_bpar) then
×
2362
       field_sum = field_sum + bpar(ig_use, :, :)
×
2363
       field_sum_new = field_sum_new + bparnew(ig_use, :, :)
×
2364
    end if
2365

2366
    ! Determine what tolerance to use
2367
    tol_use = get_option_with_default(tolerance, omegatol)
×
2368

2369
    ! Decide what size float we consider too small to divide by
2370
    ! Note this uses tiny rather than epsilon in order to allow
2371
    ! us to track modes with low amplitudes.
2372
    if (is_zero(tol_use)) then
×
2373
       too_small = tiny(0.0)
×
2374
    else
2375
       too_small = tiny(0.0) * 1000. / abs(tol_use)
×
2376
    end if
2377

2378
    ! Use where to guard against dividing by tool small a number
2379
    ! Note we don't divide by field_sum_new so we probably don't need to
2380
    ! check that here, just field_sum.
2381
    where (abs(field_sum) < too_small .or. abs(field_sum_new) < too_small)
×
2382
       omega_inst = 0.0
×
2383
    elsewhere
2384
       omega_inst = log(field_sum_new / field_sum) * zi / code_dt
×
2385
    end where
2386

2387
  end function calculate_instantaneous_omega
×
2388

2389
  !> Calculate the time-averaged complex frequency, check convergence
2390
  !> criterion, and numerical instability criterion.
2391
  !>
2392
  !> Time average is over previous [[gs2_diagnostics_knobs:navg]] timesteps
2393
  subroutine get_omegaavg (istep, exit, omegaavg, debopt)
×
2394
    use kt_grids, only: ntheta0, naky
2395
    use gs2_time, only: code_dt, user_time
2396
    use optionals, only: get_option_with_default
2397
    implicit none
2398
    !> The current timestep
2399
    integer, intent (in) :: istep
2400
    !> Should the simulation exit?
2401
    !> FIXME: This could be `intent(out)` only
2402
    logical, intent (in out) :: exit
2403
    !> The time-averaged complex frequency
2404
    complex, dimension (:,:), intent (out) :: omegaavg
2405
    !> If true, write some debug messages
2406
    logical, intent (in), optional :: debopt
2407
    logical :: debug
2408
    debug = get_option_with_default(debopt, .false.)
×
2409
    if (debug) write(6,*) "get_omeaavg: allocate domega"
×
2410
    if (.not. allocated(domega)) allocate(domega(navg,ntheta0,naky))
×
2411
    if (debug) write(6,*) "get_omeaavg: start"
×
2412
    if (debug) write(6,*) "get_omeaavg: set omegahist"
×
2413

2414
    ! Get and store the current instantaneous omega
2415
    omegahist(mod(istep, navg), :, :) = calculate_instantaneous_omega(igomega, omegatol)
×
2416

2417
    if (debug) write(6,*) "get_omeaavg: set omegahist at istep = 0"
×
2418
    !During initialisation fieldnew==field but floating error can lead to finite omegahist
2419
    !Force omegahist=0 here to avoid erroneous values.
2420
    !Could think about forcing omegahist=0 where abs(omegahist)<tol
2421
    if(istep==0) omegahist(:,:,:)=0.0
×
2422

2423
    if (debug) write(6,*) "get_omeaavg: set omegaavg"
×
2424
    omegaavg = sum(omegahist/real(navg),dim=1)
×
2425
    if (debug) write(6,*) "get_omegaavg: omegaavg=",omegaavg
×
2426
    if ((istep > navg) .and.(user_time >= omega_checks_start)) then
×
2427
       domega = spread(omegaavg,1,navg) - omegahist
×
2428
       if (all(sqrt(sum(abs(domega)**2/real(navg),dim=1)) &
×
2429
            .le. min(abs(omegaavg),1.0)*omegatol)) &
×
2430
            then
2431
          if (write_ascii) write (out_unit, "('*** omega converged')")
×
2432
          if (exit_when_converged) exit = .true.
×
2433
       end if
2434

2435
       if (any(abs(omegaavg)*code_dt > omegatinst)) then
×
2436
          if (write_ascii) write (out_unit, "('*** numerical instability detected')") 
×
2437
          exit = .true.
×
2438
       end if
2439

2440
       if (all(aimag(omegaavg) < damped_threshold)) then
×
2441
          if (write_ascii) write (out_unit, "('*** all modes strongly damped')")
×
2442
          exit = .true.
×
2443
       end if
2444
    end if
2445
    if (debug) write(6,*) "get_omegaavg: done"
×
2446
  end subroutine get_omegaavg
×
2447

2448
  !> Summed magnitude of all the fields
2449
  subroutine phinorm (phitot)
×
2450
    use fields_arrays, only: phinew, aparnew, bparnew
2451
    use theta_grid, only: theta
2452
    use kt_grids, only: naky, ntheta0
2453
    use constants, only: twopi
2454
    use integration, only: trapezoidal_integration
2455
    implicit none
2456
    real, dimension (:,:), intent (out) :: phitot
2457
    integer :: ik, it
2458
    do ik = 1, naky
×
2459
       do it = 1, ntheta0
×
2460
          phitot(it, ik) = trapezoidal_integration(theta, &
×
2461
               (abs(phinew(:, it, ik))**2 + abs(aparnew(:, it, ik))**2 &
×
2462
               + abs(bparnew(:, it, ik))**2)) / twopi
×
2463
       end do
2464
    end do
2465
  end subroutine phinorm
×
2466

2467
  !> FIXME : Add documentation
2468
  !>
2469
  !> @note : We should probably consider writing this to take the
2470
  !> array to ensemble average
2471
  !>
2472
  !> @note : We don't appear to actually average here, just sum.
2473
  subroutine ensemble_average (nensembles, time_int, start_time_in)
×
2474
    use mp, only: scope, allprocs, group_to_all, broadcast
2475
    use gs2_time, only: user_time
2476
    use species, only: nspec
2477
    use optionals, only: get_option_with_default
2478
    implicit none
2479
    integer, intent (in) :: nensembles
2480
    real, intent (out) :: time_int
2481
    real, intent(in), optional :: start_time_in
2482
    real, dimension (nensembles) :: dt_global
×
2483
    real, dimension (nensembles,nspec) :: pflx_global, qflx_global, heat_global, vflx_global
×
2484
    real :: start_time
2485
    start_time = get_option_with_default(start_time_in, 0.0)
×
2486
    time_int = user_time - start_time
×
2487
    call scope (allprocs)
×
2488
    call group_to_all (time_int, dt_global, nensembles)
×
2489
    call broadcast (dt_global)
×
2490
    time_int = sum(dt_global)
×
2491
    call group_to_all (pflux_avg, pflx_global, nensembles)
×
2492
    call group_to_all (qflux_avg, qflx_global, nensembles)
×
2493
    call group_to_all (vflux_avg, vflx_global, nensembles)
×
2494

2495
    call broadcast (pflx_global)
×
2496
    call broadcast (qflx_global)
×
2497
    call broadcast (vflx_global)
×
2498
    pflux_avg = sum(pflx_global, dim = 1)
×
2499
    qflux_avg = sum(qflx_global, dim = 1)
×
2500
    vflux_avg = sum(vflx_global, dim = 1)
×
2501
    if (write_heating) then
×
2502
       call group_to_all (heat_avg, heat_global, nensembles)
×
2503
       call broadcast (heat_global)
×
2504
       heat_avg = sum(heat_global, dim = 1)
×
2505
    end if
2506
    ! Shouldn't we "rescope" on entry to leave us in the same scope that we
2507
    ! entered in?
2508
  end subroutine ensemble_average
×
2509

2510
  !> FIXME : Add documentation
2511
  !> @note: Should look at moving this and weighted_ky_sum to volume_averages
2512
  subroutine get_volume_average (f, favg)
×
2513
    implicit none
2514
    real, dimension (:,:), intent (in) :: f
2515
    real, intent (out) :: favg
2516
    favg = sum(weighted_ky_sum(f))
×
2517
  end subroutine get_volume_average
×
2518

2519
  !> Sum a {kx,ky} array over ky accounting for non-uniform weighting arising
2520
  !> from gs2's non-standard Fourier coefficients:
2521
  !> ky=0 modes have correct amplitudes; rest must be scaled
2522
  !> note contrast with scaling factors in FFT routines.
2523
  !>
2524
  !>  fac values here arise because gs2's Fourier coefficients, F_k^gs2, not standard form:
2525
  !>          i.e. f(x) = f_k e^(i k.x)
2526
  !>  With standard Fourier coeffs in gs2, we would instead need:  fac=2.0 for ky > 0
2527
  !>      (fac=2.0 would account ky<0 contributions, not stored due to reality condition)
2528
  pure function weighted_ky_sum(f) result(ky_sum)
×
2529
    use kt_grids, only: naky, ntheta0, aky
2530
    use warning_helpers, only: is_zero
2531
    implicit none
2532
    real, dimension(:, :), intent (in) :: f
2533
    real, dimension(ntheta0) :: ky_sum
2534
    real :: fac
2535
    integer :: ik
2536
    ky_sum = 0.
×
2537
    do ik = 1, naky
×
2538
       fac = 0.5
×
2539
       if (is_zero(aky(ik))) fac = 1.0
×
2540
       ky_sum = ky_sum + f(:, ik) * fac
×
2541
    end do
2542
  end function weighted_ky_sum
×
2543

2544
  !> Calculate the correlation function over the extended domain
2545
  subroutine correlation_extend (cfnc, phi2extend, ntg_extend)
×
2546
    use fields_arrays, only: phinew
2547
    use theta_grid, only: ntgrid, jacob, delthet, ntgrid
2548
    use kt_grids, only: ntheta0, naky, jtwist
2549
    implicit none
2550
    real, dimension (:,:,:), intent (out) :: phi2extend
2551
    complex, dimension (:,:,:), intent (out) :: cfnc
2552
    integer, intent(in) :: ntg_extend
2553
    integer :: ig, it, ik, im, igmod, itshift, nconnect, offset
2554
    real, dimension (:), allocatable :: dl_over_b
×
2555
    complex, dimension (:,:,:), allocatable :: phiextend
×
2556
    complex, dimension (:,:), allocatable :: phir
×
2557

2558
    allocate (dl_over_b(2*ntgrid+1))
×
2559
    dl_over_b = delthet * jacob
×
2560

2561
    allocate (phiextend(ntg_extend,ntheta0,naky)) ; phiextend = 0.0
×
2562
    allocate (phir(-ntgrid:ntgrid,ntheta0))
×
2563

2564
    cfnc = 0.0 ; phiextend = 0.0
×
2565
    offset = ((ntheta0-1)/jtwist)*(2*ntgrid+1)/2
×
2566
    call reorder_kx (phinew(:, :, 1), phir(:, :))
×
2567
    phiextend(offset+1:offset+(2*ntgrid+1),:,1) = phir
×
2568
    do it = 1, ntheta0
×
2569
       do im = 1, 2*ntgrid+1
×
2570
          do ig = im+offset, offset+(2*ntgrid+1)
×
2571
             igmod = mod(ig-offset-1,2*ntgrid+1)+1
×
2572
             cfnc(im,it,1) = cfnc(im,it,1) &
×
2573
                  + phiextend(ig,it,1)*conjg(phiextend(ig-im+1,it,1)) &
×
2574
                  * dl_over_b(igmod)
×
2575
          end do
2576
       end do
2577
       if (abs(cfnc(1, it, 1)) > epsilon(0.0)) then
×
2578
         cfnc(:, it, 1) = cfnc(:, it, 1) / cfnc(1, it, 1)
×
2579
       end if
2580
    end do
2581

2582
    do ik = 2, naky
×
2583
       call reorder_kx (phinew(:, :, ik), phir(:, :))
×
2584
       ! shift in kx due to parallel boundary condition
2585
       ! also the number of independent theta0s
2586
       itshift = jtwist*(ik-1)
×
2587
       do it = 1, min(itshift,ntheta0)
×
2588
          ! number of connections between kx's
2589
          nconnect = (ntheta0-it)/itshift
×
2590
          ! shift of theta index to account for fact that not all ky's
2591
          ! have same length in extended theta
2592
          offset = (2*ntgrid+1)*((ntheta0-1)/jtwist-nconnect)/2
×
2593
          do ig = 1, nconnect+1
×
2594
             phiextend(offset+(ig-1)*(2*ntgrid+1)+1:offset+ig*(2*ntgrid+1),it,ik) &
×
2595
                  = phir(:,ntheta0-it-(ig-1)*itshift+1)
×
2596
          end do
2597
          do im = 1, (2*ntgrid+1)*(nconnect+1)
×
2598
             do ig = im+offset, offset+(2*ntgrid+1)*(nconnect+1)
×
2599
                igmod = mod(ig-offset-1,2*ntgrid+1)+1
×
2600
                cfnc(im,it,ik) = cfnc(im,it,ik) &
×
2601
                     + phiextend(ig,it,ik)*conjg(phiextend(ig-im+1,it,ik)) &
×
2602
                     * dl_over_b(igmod)
×
2603
             end do
2604
          end do
2605
          cfnc(:,it,ik) = cfnc(:,it,ik) / cfnc(1,it,ik)
×
2606
       end do
2607
    end do
2608

2609
    phi2extend = real(phiextend*conjg(phiextend))
×
2610

2611
    deallocate (dl_over_b, phir, phiextend)
×
2612
  end subroutine correlation_extend
×
2613

2614
  !> Go from kx_ind = [0, 1, ..., N, -N, ...., -1]
2615
  !> to [-N, ....,-1,0,1,....,N]
2616
  subroutine reorder_kx (unord, ord)
×
2617
    use kt_grids, only: ntheta0
2618
    implicit none
2619
    complex, dimension (:, :), intent (in) :: unord
2620
    complex, dimension (:, :), intent (out) :: ord
2621
    ord = cshift(unord, -ntheta0 / 2, dim = 2)
×
2622
  end subroutine reorder_kx
×
2623

2624
  !> Calculate the correlation function on the physical domain
2625
  subroutine correlation (cfnc_2pi)
×
2626
    use kt_grids, only: naky, ntheta0
2627
    use theta_grid, only: ntgrid
2628
    use fields_arrays, only: phinew
2629
    implicit none
2630
    complex, dimension (-ntgrid:,:), intent (out) :: cfnc_2pi
2631
    integer :: ik, it, ig
2632
    real :: fac
2633

2634
    cfnc_2pi = 0.0
×
2635

2636
    do ik = 1, naky
×
2637
       if (ik==1) then
×
2638
          fac = 1.0
×
2639
       else
2640
          fac = 0.5
×
2641
       end if
2642
       do it = 1, ntheta0
×
2643
          do ig = -ntgrid, ntgrid
×
2644
             cfnc_2pi(ig,ik) = cfnc_2pi(ig,ik) + phinew(0,it,ik)*conjg(phinew(ig,it,ik))*fac
×
2645
          end do
2646
       end do
2647
    end do
2648

2649
  end subroutine correlation
×
2650

2651
  !> Write \(g(v_\parallel,v_\perp)\) at `ik=it=is=1, ig=0` to text file `<runname>.dist`
2652
  subroutine do_write_f (unit)
×
2653
    use mp, only: proc0, send, receive
2654
    use gs2_layouts, only: g_lo, ik_idx, it_idx, is_idx, il_idx, ie_idx, idx_local, proc_id
2655
    use le_grids, only: al, energy, forbid, negrid, nlambda, speed
2656
    use theta_grid, only: bmag
2657
    use dist_fn_arrays, only: gnew
2658
    use species, only: nspec
2659
    integer, intent(in) :: unit
2660
    integer :: iglo, ik, it, is
2661
    integer :: ie, il, ig
2662
    real :: vpa, vpe
2663
    complex, dimension(2) :: gtmp
2664
    real, dimension (:,:), allocatable, save :: xpts
2665
    real, dimension (:), allocatable, save :: ypts
2666
    !> Change argument of bmag depending on which theta you want to write out
2667
    integer, parameter :: ig_index = 0
2668
    if (.not. allocated(xpts)) then
×
2669
       allocate(xpts(negrid,nspec))
×
2670
       allocate(ypts(nlambda))
×
2671

2672
       ! should really just get rid of xpts and ypts
2673
       xpts = speed
×
2674
       ypts = 0.0
×
2675

2676
       do il=1,nlambda
×
2677
          ypts(il) = sqrt(max(0.0,1.0-al(il)*bmag(ig_index)))
×
2678
       end do
2679

2680
       if (proc0) then
×
2681
          write(unit, *) negrid*nlambda
×
2682
       end if
2683
    endif
2684

2685
    do iglo = g_lo%llim_world, g_lo%ulim_world
×
2686
       ik = ik_idx(g_lo, iglo) ; if (ik /= 1) cycle
×
2687
       it = it_idx(g_lo, iglo) ; if (it /= 1) cycle
×
2688
       is = is_idx(g_lo, iglo) ; if (is /= 1) cycle
×
2689
       ie = ie_idx(g_lo, iglo)
×
2690
       ig = ig_index
×
2691
       il = il_idx(g_lo, iglo)
×
2692
       if (idx_local (g_lo, ik, it, il, ie, is)) then
×
2693
          if (proc0) then
×
2694
             gtmp = gnew(ig, :, iglo)
×
2695
          else
2696
             call send (gnew(ig,:,iglo), 0)
×
2697
          endif
2698
       else if (proc0) then
×
2699
          call receive (gtmp, proc_id(g_lo, iglo))
×
2700
       endif
2701
       if (proc0) then
×
2702
          if (.not. forbid(ig, il)) then
×
2703
             vpa = sqrt(energy(ie,is)*max(0.0, 1.0-al(il)*bmag(ig)))
×
2704
             vpe = sqrt(energy(ie,is)*al(il)*bmag(ig))
×
2705
             write (unit, "(8(1x,e13.6))") vpa, vpe, energy(ie,is), al(il), &
×
2706
                  xpts(ie,is), ypts(il), real(gtmp(1)), real(gtmp(2))
×
2707
          end if
2708
       end if
2709
    end do
2710
    if (proc0) write (unit, *)
×
2711
  end subroutine do_write_f
×
2712

2713
  !> Write distribution function (\(g\) or possibly \(f\)?) in real space to text file
2714
  !> "<runname>.yxdist"
2715
  subroutine do_write_fyx (unit, phi, bpar)
×
2716
    use mp, only: proc0, send, receive, barrier
2717
    use gs2_layouts, only: il_idx, ig_idx, ik_idx, it_idx, is_idx, isign_idx, ie_idx
2718
    use gs2_layouts, only: idx_local, proc_id, yxf_lo, accelx_lo, g_lo
2719
    use le_grids, only: al, energy=>energy_maxw, forbid, negrid, nlambda
2720
    use theta_grid, only: bmag, ntgrid
2721
    use dist_fn_arrays, only: gnew, g_adjust, g_work, to_g_gs2, from_g_gs2
2722
    use nonlinear_terms, only: accelerated
2723
    use gs2_transforms, only: transform2, init_transforms
2724
    use array_utils, only: zero_array, copy
2725
#ifdef SHMEM
2726
    use shm_mpi3, only: shm_alloc, shm_free
2727
#endif
2728
    integer, intent(in) :: unit
2729
    !> Electrostatic potential and parallel magnetic field
2730
    complex, dimension (-ntgrid:,:,:), intent (in) :: phi, bpar
2731
    real, dimension (:,:), allocatable :: grs, gzf
×
2732
#ifndef SHMEM
2733
    real, dimension (:,:,:), allocatable :: agrs, agzf
×
2734
#else
2735
    real, dimension (:,:,:), pointer, contiguous :: agrs => null(), agzf => null()
2736
    complex, dimension(:,:,:), pointer, contiguous :: g0_ptr => null()
2737
#endif
2738
    real, dimension (:), allocatable :: agp0, agp0zf
×
2739
    real :: gp0, gp0zf
2740
    integer :: ig, it, ik, il, ie, is, iyxlo, isign, iaclo, iglo, acc
2741
    logical, save :: first = .true.
2742

2743
    if (accelerated) then
×
2744
#ifndef SHMEM
2745
       allocate (agrs(2*ntgrid+1, 2, accelx_lo%llim_proc:accelx_lo%ulim_alloc))
×
2746
       allocate (agzf(2*ntgrid+1, 2, accelx_lo%llim_proc:accelx_lo%ulim_alloc))
×
2747
#else
2748
       call shm_alloc(agrs, [1, 2*ntgrid+1, 1, 2, accelx_lo%llim_proc, accelx_lo%ulim_alloc])
2749
       call shm_alloc(agzf, [1, 2*ntgrid+1, 1, 2, accelx_lo%llim_proc, accelx_lo%ulim_alloc])
2750
       call shm_alloc(g0_ptr, [1, 2*ntgrid+1, 1, 2, accelx_lo%llim_proc, accelx_lo%ulim_alloc])
2751
#endif
2752
       allocate (agp0(2), agp0zf(2))
×
2753
       call zero_array(agrs); call zero_array(agzf); agp0 = 0.0; agp0zf = 0.0
×
2754
    else
2755
#ifdef SHMEM
2756
       g0_ptr => g_work
2757
#endif
2758
       allocate (grs(yxf_lo%ny, yxf_lo%llim_proc:yxf_lo%ulim_alloc))
×
2759
       allocate (gzf(yxf_lo%ny, yxf_lo%llim_proc:yxf_lo%ulim_alloc))
×
2760
       call zero_array(grs); call zero_array(gzf); gp0 = 0.0; gp0zf = 0.0
×
2761
    end if
2762

2763
    if (first) then
×
2764
       if (proc0) then
×
2765
          acc = 0
×
2766
          if (accelerated) acc = 1
×
2767
          write(unit,*) 2*negrid*nlambda, bmag(0), acc
×
2768
       end if
2769

2770
       first = .false.
×
2771
    end if
2772

2773
    call g_adjust (gnew, phi, bpar, direction = from_g_gs2)
×
2774

2775
#ifndef SHMEM
2776
    call copy(gnew, g_work)
×
2777
#else
2778
    g0_ptr = gnew
2779
#endif
2780

2781
#ifndef SHMEM
2782
    if (accelerated) then
×
2783
       call transform2 (g_work, agrs)
×
2784
    else
2785
       call transform2 (g_work, grs)
×
2786
    end if
2787

2788
    call zero_array(g_work)
×
2789
    do iglo=g_lo%llim_proc, g_lo%ulim_proc
×
2790
       ik = ik_idx(g_lo,iglo)
×
2791
       if (ik == 1) g_work(:,:,iglo) = gnew(:,:,iglo)
×
2792
    end do
2793

2794
    call g_adjust (gnew, phi, bpar, direction = to_g_gs2)
×
2795

2796
    if (accelerated) then
×
2797
       call transform2 (g_work, agzf)
×
2798
    else
2799
       call transform2 (g_work, gzf)
×
2800
    end if
2801
#else
2802
    if (accelerated) then
2803
       call transform2 (g0_ptr, agrs)
2804
    else
2805
       call transform2 (g0_ptr, grs)
2806
    end if
2807

2808
    g0_ptr = 0.0
2809
    do iglo=g_lo%llim_proc, g_lo%ulim_proc
2810
       ik = ik_idx(g_lo,iglo)
2811
       if (ik == 1) g0_ptr(:,:,iglo) = gnew(:,:,iglo)
2812
    end do
2813

2814
    call g_adjust (gnew, phi, bpar, direction = to_g_gs2)
2815

2816
    if (accelerated) then
2817
       call transform2 (g0_ptr, agzf)
2818
    else
2819
       call transform2 (g0_ptr, gzf)
2820
    end if
2821
#endif
2822

2823
    if (accelerated) then
×
2824
       do iaclo=accelx_lo%llim_world, accelx_lo%ulim_world
×
2825
          it = it_idx(accelx_lo, iaclo)
×
2826
          ik = ik_idx(accelx_lo, iaclo)
×
2827
          if (it == 1 .and. ik == 1) then
×
2828
             il = il_idx(accelx_lo, iaclo)
×
2829
             ig = 0
×
2830
             if (.not. forbid(ig,il)) then
×
2831
                ie = ie_idx(accelx_lo, iaclo)
×
2832
                is = is_idx(accelx_lo, iaclo)
×
2833

2834
                if (proc0) then
×
2835
                   if (.not. idx_local(accelx_lo, ik, it, il, ie, is)) then
×
2836
                      call receive (agp0, proc_id(accelx_lo, iaclo))
×
2837
                      call receive (agp0zf, proc_id(accelx_lo, iaclo))
×
2838
                   else
2839
                      agp0 = agrs(ig+ntgrid+1, :, iaclo)
×
2840
                      agp0zf = agzf(ig+ntgrid+1, :, iaclo)
×
2841
                   end if
2842
                else if (idx_local(accelx_lo, ik, it, il, ie, is)) then
×
2843
                   call send (agrs(ig+ntgrid+1, :, iaclo), 0)
×
2844
                   call send (agzf(ig+ntgrid+1, :, iaclo), 0)
×
2845
                end if
2846

2847
                if (proc0) then
×
2848
                   write (unit, "(6(1x,e13.6))") energy(ie), al(il), &
×
2849
                        agp0(1), agp0(2), agp0zf(1), agp0zf(2)
×
2850
                end if
2851
             end if
2852
          end if
2853
          call barrier
×
2854
       end do
2855
       deallocate(agp0, agp0zf)
×
2856
#ifndef SHMEM
2857
       deallocate(agrs, agzf)
×
2858
#else
2859
       call shm_free(agrs)
2860
       call shm_free(agzf)
2861
       call shm_free(g0_ptr)
2862
#endif
2863
    else
2864
       do iyxlo=yxf_lo%llim_world, yxf_lo%ulim_world
×
2865

2866
          ig = ig_idx(yxf_lo, iyxlo)
×
2867
          it = it_idx(yxf_lo, iyxlo)
×
2868
          if (ig == 0 .and. it == 1) then
×
2869
             il = il_idx(yxf_lo, iyxlo)
×
2870
             if (.not. forbid(ig,il)) then
×
2871
                ik = 1
×
2872
                ie = ie_idx(yxf_lo, iyxlo)
×
2873
                is = is_idx(yxf_lo, iyxlo)
×
2874
                isign = isign_idx(yxf_lo, iyxlo)
×
2875

2876
                if (proc0) then
×
2877
                   if (.not. idx_local(yxf_lo, ig, isign, it, il, ie, is)) then
×
2878
                      call receive (gp0, proc_id(yxf_lo, iyxlo))
×
2879
                      call receive (gp0zf, proc_id(yxf_lo, iyxlo))
×
2880
                   else
2881
                      gp0 = grs(ik, iyxlo)
×
2882
                      gp0zf = gzf(ik, iyxlo)
×
2883
                   end if
2884
                else if (idx_local(yxf_lo, ig, isign, it, il, ie, is)) then
×
2885
                   call send (grs(ik, iyxlo), 0)
×
2886
                   call send (gzf(ik, iyxlo), 0)
×
2887
                end if
2888

2889
                if (proc0) then
×
2890
                   write (unit, "(4(1x,e13.6),i8)") energy(ie), al(il), &
×
2891
                        gp0, gp0zf, isign
×
2892
                end if
2893
             end if
2894
          end if
2895
          call barrier
×
2896
       end do
2897
       deallocate (grs, gzf)
×
2898
    end if
2899

2900
    if (proc0) flush (unit)
×
2901

2902
    if (proc0) then
×
2903
       write(unit,*)
×
2904
    end if
2905

2906
  end subroutine do_write_fyx
×
2907
end module gs2_diagnostics
×
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