-
Notifications
You must be signed in to change notification settings - Fork 4
/
velocity_moments.jl
1574 lines (1487 loc) · 71.7 KB
/
velocity_moments.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
"""
"""
module velocity_moments
export integrate_over_vspace
export integrate_over_positive_vpa, integrate_over_negative_vpa
export integrate_over_positive_vz, integrate_over_negative_vz
export create_moments_chrg, create_moments_ntrl
export update_moments!
export update_density!
export update_upar!
export update_ppar!
export update_qpar!
export reset_moments_status!
export moments_chrg_substruct, moments_ntrl_substruct
export update_neutral_density!
export update_neutral_uz!
export update_neutral_ur!
export update_neutral_uzeta!
export update_neutral_pz!
export update_neutral_pr!
export update_neutral_pzeta!
export update_neutral_qz!
export update_chodura!
using ..type_definitions: mk_float
using ..array_allocation: allocate_shared_float, allocate_bool, allocate_float
using ..calculus: integral
using ..communication
using ..derivatives: derivative_z!
using ..derivatives: derivative_r!
using ..looping
#global tmpsum1 = 0.0
#global tmpsum2 = 0.0
#global dens_hist = zeros(17,1)
#global n_hist = 0
"""
"""
struct moments_charged_substruct
# this is the particle density
dens::MPISharedArray{mk_float,3}
# flag that keeps track of if the density needs updating before use
# Note: may not be set for all species on this process, but this process only ever
# sets/uses the value for the same subset of species. This means dens_update does
# not need to be a shared memory array.
dens_updated::Vector{Bool}
# this is the parallel flow
upar::MPISharedArray{mk_float,3}
# flag that keeps track of whether or not upar needs updating before use
# Note: may not be set for all species on this process, but this process only ever
# sets/uses the value for the same subset of species. This means upar_update does
# not need to be a shared memory array.
upar_updated::Vector{Bool}
# this is the parallel pressure
ppar::MPISharedArray{mk_float,3}
# flag that keeps track of whether or not ppar needs updating before use
# Note: may not be set for all species on this process, but this process only ever
# sets/uses the value for the same subset of species. This means ppar_update does
# not need to be a shared memory array.
ppar_updated::Vector{Bool}
# this is the parallel heat flux
qpar::MPISharedArray{mk_float,3}
# flag that keeps track of whether or not qpar needs updating before use
# Note: may not be set for all species on this process, but this process only ever
# sets/uses the value for the same subset of species. This means qpar_update does
# not need to be a shared memory array.
qpar_updated::Vector{Bool}
# this is the thermal speed based on the parallel temperature Tpar = ppar/dens: vth = sqrt(2*Tpar/m)
vth::MPISharedArray{mk_float,3}
# generalised Chodura integrals for the lower and upper plates
chodura_integral_lower::MPISharedArray{mk_float,2}
chodura_integral_upper::MPISharedArray{mk_float,2}
# if evolve_ppar = true, then the velocity variable is (vpa - upa)/vth, which introduces
# a factor of vth for each power of wpa in velocity space integrals.
# v_norm_fac accounts for this: it is vth if using the above definition for the parallel velocity,
# and it is one otherwise
v_norm_fac::Union{MPISharedArray{mk_float,3},Nothing}
# this is the upwinded z-derivative of the particle density
ddens_dz_upwind::Union{MPISharedArray{mk_float,3},Nothing}
# this is the second-z-derivative of the particle density
d2dens_dz2::Union{MPISharedArray{mk_float,3},Nothing}
# this is the z-derivative of the parallel flow
dupar_dz::Union{MPISharedArray{mk_float,3},Nothing}
# this is the upwinded z-derivative of the parallel flow
dupar_dz_upwind::Union{MPISharedArray{mk_float,3},Nothing}
# this is the second-z-derivative of the parallel flow
d2upar_dz2::Union{MPISharedArray{mk_float,3},Nothing}
# this is the z-derivative of the parallel pressure
dppar_dz::Union{MPISharedArray{mk_float,3},Nothing}
# this is the upwinded z-derivative of the parallel pressure
dppar_dz_upwind::Union{MPISharedArray{mk_float,3},Nothing}
# this is the second-z-derivative of the parallel pressure
d2ppar_dz2::Union{MPISharedArray{mk_float,3},Nothing}
# this is the z-derivative of the parallel heat flux
dqpar_dz::Union{MPISharedArray{mk_float,3},Nothing}
# this is the z-derivative of the thermal speed based on the parallel temperature Tpar = ppar/dens: vth = sqrt(2*Tpar/m)
dvth_dz::Union{MPISharedArray{mk_float,3},Nothing}
end
"""
"""
struct moments_neutral_substruct
# this is the particle density
dens::MPISharedArray{mk_float,3}
# flag that keeps track of if the density needs updating before use
# Note: may not be set for all species on this process, but this process only ever
# sets/uses the value for the same subset of species. This means dens_update does
# not need to be a shared memory array.
dens_updated::Vector{Bool}
# this is the particle mean velocity in z
uz::MPISharedArray{mk_float,3}
# flag that keeps track of if uz needs updating before use
uz_updated::Vector{Bool}
# this is the particle mean velocity in r
ur::MPISharedArray{mk_float,3}
# flag that keeps track of if ur needs updating before use
ur_updated::Vector{Bool}
# this is the particle mean velocity in zeta
uzeta::MPISharedArray{mk_float,3}
# flag that keeps track of if uzeta needs updating before use
uzeta_updated::Vector{Bool}
# this is the zz particle pressure tensor component
pz::MPISharedArray{mk_float,3}
# flag that keeps track of if pz needs updating before use
pz_updated::Vector{Bool}
# this is the rr particle pressure tensor component
pr::MPISharedArray{mk_float,3}
# flag that keeps track of if pr needs updating before use
pr_updated::Vector{Bool}
# this is the zetazeta particle pressure tensor component
pzeta::MPISharedArray{mk_float,3}
# flag that keeps track of if pzeta needs updating before use
pzeta_updated::Vector{Bool}
# this is the total (isotropic) particle pressure
ptot::MPISharedArray{mk_float,3}
# this is the heat flux along z
qz::MPISharedArray{mk_float,3}
# flag that keeps track of if qz needs updating before use
qz_updated::Vector{Bool}
# this is the thermal speed based on the temperature T = ptot/dens: vth = sqrt(2*T/m)
vth::MPISharedArray{mk_float,3}
# if evolve_ppar = true, then the velocity variable is (vz - uz)/vth, which introduces
# a factor of vth for each power of wz in velocity space integrals.
# v_norm_fac accounts for this: it is vth if using the above definition for the parallel velocity,
# and it is one otherwise
v_norm_fac::MPISharedArray{mk_float,3}
# this is the z-derivative of the particle density
ddens_dz_upwind::Union{MPISharedArray{mk_float,3},Nothing}
# this is the second-z-derivative of the particle density
d2dens_dz2::Union{MPISharedArray{mk_float,3},Nothing}
# this is the z-derivative of the particle mean velocity in z
duz_dz::Union{MPISharedArray{mk_float,3},Nothing}
# this is the upwinded z-derivative of the particle mean velocity in z
duz_dz_upwind::Union{MPISharedArray{mk_float,3},Nothing}
# this is the second-z-derivative of the particle mean velocity in z
d2uz_dz2::Union{MPISharedArray{mk_float,3},Nothing}
# this is the z-derivative of the zz particle pressure tensor component
dpz_dz::Union{MPISharedArray{mk_float,3},Nothing}
# this is the upwinded z-derivative of the zz particle pressure tensor component
dpz_dz_upwind::Union{MPISharedArray{mk_float,3},Nothing}
# this is the second-z-derivative of the zz particle pressure tensor component
d2pz_dz2::Union{MPISharedArray{mk_float,3},Nothing}
# this is the z-derivative of the thermal speed based on the temperature T = ptot/dens: vth = sqrt(2*T/m)
dvth_dz::Union{MPISharedArray{mk_float,3},Nothing}
# this is the z-derivative of the heat flux along z
dqz_dz::Union{MPISharedArray{mk_float,3},Nothing}
end
"""
"""
function create_moments_charged(nz, nr, n_species, evolve_density, evolve_upar,
evolve_ppar, numerical_dissipation)
# allocate array used for the particle density
density = allocate_shared_float(nz, nr, n_species)
# allocate array of Bools that indicate if the density is updated for each species
density_updated = allocate_bool(n_species)
density_updated .= false
# allocate array used for the parallel flow
parallel_flow = allocate_shared_float(nz, nr, n_species)
# allocate array of Bools that indicate if the parallel flow is updated for each species
parallel_flow_updated = allocate_bool(n_species)
parallel_flow_updated .= false
# allocate array used for the parallel pressure
parallel_pressure = allocate_shared_float(nz, nr, n_species)
# allocate array of Bools that indicate if the parallel pressure is updated for each species
parallel_pressure_updated = allocate_bool(n_species)
parallel_pressure_updated .= false
# allocate array used for the parallel flow
parallel_heat_flux = allocate_shared_float(nz, nr, n_species)
# allocate array of Bools that indicate if the parallel flow is updated for each species
parallel_heat_flux_updated = allocate_bool(n_species)
parallel_heat_flux_updated .= false
# allocate array of Bools that indicate if the parallel flow is updated for each species
# allocate array used for the thermal speed
thermal_speed = allocate_shared_float(nz, nr, n_species)
chodura_integral_lower = allocate_shared_float(nr, n_species)
chodura_integral_upper = allocate_shared_float(nr, n_species)
if evolve_ppar
v_norm_fac = thermal_speed
else
v_norm_fac = allocate_shared_float(nz, nr, n_species)
@serial_region begin
v_norm_fac .= 1.0
end
end
if evolve_density
ddens_dz_upwind = allocate_shared_float(nz, nr, n_species)
else
ddens_dz_upwind = nothing
end
if evolve_density &&
numerical_dissipation.moment_dissipation_coefficient > 0.0
d2dens_dz2 = allocate_shared_float(nz, nr, n_species)
else
d2dens_dz2 = nothing
end
if evolve_density || evolve_upar || evolve_ppar
dupar_dz = allocate_shared_float(nz, nr, n_species)
else
dupar_dz = nothing
end
if evolve_upar
dupar_dz_upwind = allocate_shared_float(nz, nr, n_species)
else
dupar_dz_upwind = nothing
end
if evolve_upar &&
numerical_dissipation.moment_dissipation_coefficient > 0.0
d2upar_dz2 = allocate_shared_float(nz, nr, n_species)
else
d2upar_dz2 = nothing
end
if evolve_upar
dppar_dz = allocate_shared_float(nz, nr, n_species)
else
dppar_dz = nothing
end
if evolve_ppar
dppar_dz_upwind = allocate_shared_float(nz, nr, n_species)
d2ppar_dz2 = allocate_shared_float(nz, nr, n_species)
dqpar_dz = allocate_shared_float(nz, nr, n_species)
dvth_dz = allocate_shared_float(nz, nr, n_species)
else
dppar_dz_upwind = nothing
d2ppar_dz2 = nothing
dqpar_dz = nothing
dvth_dz = nothing
end
# return struct containing arrays needed to update moments
return moments_charged_substruct(density, density_updated, parallel_flow,
parallel_flow_updated, parallel_pressure, parallel_pressure_updated,
parallel_heat_flux, parallel_heat_flux_updated, thermal_speed,
chodura_integral_lower, chodura_integral_upper, v_norm_fac,
ddens_dz_upwind, d2dens_dz2, dupar_dz, dupar_dz_upwind, d2upar_dz2, dppar_dz,
dppar_dz_upwind, d2ppar_dz2, dqpar_dz, dvth_dz)
end
# neutral particles have natural mean velocities
# uz, ur, uzeta =/= upar
# and similarly for heat fluxes
# therefore separate moments object for neutrals
function create_moments_neutral(nz, nr, n_species, evolve_density, evolve_upar,
evolve_ppar, numerical_dissipation)
density = allocate_shared_float(nz, nr, n_species)
density_updated = allocate_bool(n_species)
density_updated .= false
uz = allocate_shared_float(nz, nr, n_species)
uz_updated = allocate_bool(n_species)
uz_updated .= false
ur = allocate_shared_float(nz, nr, n_species)
ur_updated = allocate_bool(n_species)
ur_updated .= false
uzeta = allocate_shared_float(nz, nr, n_species)
uzeta_updated = allocate_bool(n_species)
uzeta_updated .= false
pz = allocate_shared_float(nz, nr, n_species)
pz_updated = allocate_bool(n_species)
pz_updated .= false
pr = allocate_shared_float(nz, nr, n_species)
pr_updated = allocate_bool(n_species)
pr_updated .= false
pzeta = allocate_shared_float(nz, nr, n_species)
pzeta_updated = allocate_bool(n_species)
pzeta_updated .= false
ptot = allocate_shared_float(nz, nr, n_species)
vth = allocate_shared_float(nz, nr, n_species)
if evolve_ppar
v_norm_fac = vth
else
v_norm_fac = allocate_shared_float(nz, nr, n_species)
@serial_region begin
v_norm_fac .= 1.0
end
end
qz = allocate_shared_float(nz, nr, n_species)
qz_updated = allocate_bool(n_species)
qz_updated .= false
if evolve_density
ddens_dz_upwind = allocate_shared_float(nz, nr, n_species)
else
ddens_dz_upwind = nothing
end
if evolve_density &&
numerical_dissipation.moment_dissipation_coefficient > 0.0
d2dens_dz2 = allocate_shared_float(nz, nr, n_species)
else
d2dens_dz2 = nothing
end
if evolve_density || evolve_upar || evolve_ppar
duz_dz = allocate_shared_float(nz, nr, n_species)
else
duz_dz = nothing
end
if evolve_upar
duz_dz_upwind = allocate_shared_float(nz, nr, n_species)
else
duz_dz_upwind = nothing
end
if evolve_upar &&
numerical_dissipation.moment_dissipation_coefficient > 0.0
d2uz_dz2 = allocate_shared_float(nz, nr, n_species)
else
d2uz_dz2 = nothing
end
if evolve_upar
dpz_dz = allocate_shared_float(nz, nr, n_species)
else
dpz_dz = nothing
end
if evolve_ppar
dpz_dz_upwind = allocate_shared_float(nz, nr, n_species)
d2pz_dz2 = allocate_shared_float(nz, nr, n_species)
dqz_dz = allocate_shared_float(nz, nr, n_species)
dvth_dz = allocate_shared_float(nz, nr, n_species)
else
dpz_dz_upwind = nothing
d2pz_dz2 = nothing
dqz_dz = nothing
dvth_dz = nothing
end
# return struct containing arrays needed to update moments
return moments_neutral_substruct(density, density_updated, uz, uz_updated, ur,
ur_updated, uzeta, uzeta_updated, pz, pz_updated, pr, pr_updated, pzeta,
pzeta_updated, ptot, qz, qz_updated, vth, v_norm_fac, ddens_dz_upwind, d2dens_dz2,
duz_dz, duz_dz_upwind, d2uz_dz2, dpz_dz, dpz_dz_upwind, d2pz_dz2, dqz_dz, dvth_dz)
end
"""
calculate the updated density (dens) and parallel pressure (ppar) for all species
"""
function update_moments!(moments, ff, vpa, vperp, z, r, composition)
begin_s_r_z_region()
n_species = size(ff,5)
@boundscheck n_species == size(moments.charged.dens,3) || throw(BoundsError(moments))
@loop_s is begin
if moments.charged.dens_updated[is] == false
@views update_density_species!(moments.charged.dens[:,:,is], ff[:,:,:,:,is],
vpa, vperp, z, r)
moments.charged.dens_updated[is] = true
end
if moments.charged.upar_updated[is] == false
# Can pass moments.ppar here even though it has not been updated yet,
# because moments.ppar is only needed if evolve_ppar=true, in which case it
# will not be updated because it is not calculated from the distribution
# function
@views update_upar_species!(moments.charged.upar[:,:,is],
moments.charged.dens[:,:,is],
moments.charged.ppar[:,:,is], ff[:,:,:,:,is], vpa,
vperp, z, r, moments.evolve_density,
moments.evolve_ppar)
moments.charged.upar_updated[is] = true
end
if moments.charged.ppar_updated[is] == false
@views update_ppar_species!(moments.charged.ppar[:,:,is],
moments.charged.dens[:,:,is],
moments.charged.upar[:,:,is], ff[:,:,:,:,is], vpa,
vperp, z, r, moments.evolve_density,
moments.evolve_upar)
moments.charged.ppar_updated[is] = true
end
@loop_r_z ir iz begin
moments.charged.vth[iz,ir,is] =
sqrt(2*moments.charged.ppar[iz,ir,is]/moments.charged.dens[iz,ir,is])
end
if moments.charged.qpar_updated[is] == false
@views update_qpar_species!(moments.charged.qpar[:,:,is],
moments.charged.dens[:,:,is],
moments.charged.upar[:,:,is],
moments.charged.vth[:,:,is], ff[:,:,:,:,is], vpa,
vperp, z, r, moments.evolve_density,
moments.evolve_upar, moments.evolve_ppar)
moments.charged.qpar_updated[is] = true
end
end
return nothing
end
"""
NB: if this function is called and if dens_updated is false, then
the incoming pdf is the un-normalized pdf that satisfies int dv pdf = density
"""
function update_density!(dens, dens_updated, pdf, vpa, vperp, z, r, composition)
begin_s_r_z_region()
n_species = size(pdf,5)
@boundscheck n_species == size(dens,3) || throw(BoundsError(dens))
@loop_s is begin
if dens_updated[is] == false
@views update_density_species!(dens[:,:,is], pdf[:,:,:,:,is], vpa, vperp, z, r)
dens_updated[is] = true
end
end
end
"""
calculate the updated density (dens) for a given species;
should only be called when evolve_density = false,
in which case the vpa coordinate is vpa/c_s
"""
function update_density_species!(dens, ff, vpa, vperp, z, r)
@boundscheck vpa.n == size(ff, 1) || throw(BoundsError(ff))
@boundscheck vperp.n == size(ff, 2) || throw(BoundsError(ff))
@boundscheck z.n == size(ff, 3) || throw(BoundsError(ff))
@boundscheck r.n == size(ff, 4) || throw(BoundsError(ff))
@boundscheck z.n == size(dens, 1) || throw(BoundsError(dens))
@boundscheck r.n == size(dens, 2) || throw(BoundsError(dens))
@loop_r_z ir iz begin
# When evolve_density = false, the evolved pdf is the 'true' pdf, and the vpa
# coordinate is (dz/dt) / c_s.
# Integrating calculates n_s / N_e = (1/√π)∫d(vpa/c_s) (√π f_s c_s / N_e)
dens[iz,ir] = integrate_over_vspace(@view(ff[:,:,iz,ir]),
vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts)
end
return nothing
end
"""
NB: if this function is called and if upar_updated is false, then
the incoming pdf is the un-normalized pdf that satisfies int dv pdf = density
"""
function update_upar!(upar, upar_updated, density, ppar, pdf, vpa, vperp, z, r,
composition, evolve_density, evolve_ppar)
begin_s_r_z_region()
n_species = size(pdf,5)
@boundscheck n_species == size(upar,3) || throw(BoundsError(upar))
@loop_s is begin
if upar_updated[is] == false
@views update_upar_species!(upar[:,:,is], density[:,:,is], ppar[:,:,is],
pdf[:,:,:,:,is], vpa, vperp, z, r, evolve_density,
evolve_ppar)
upar_updated[is] = true
end
end
end
"""
calculate the updated parallel flow (upar) for a given species
"""
function update_upar_species!(upar, density, ppar, ff, vpa, vperp, z, r, evolve_density,
evolve_ppar)
@boundscheck vperp.n == size(ff, 2) || throw(BoundsError(ff))
@boundscheck z.n == size(ff, 3) || throw(BoundsError(ff))
@boundscheck r.n == size(ff, 4) || throw(BoundsError(ff))
@boundscheck z.n == size(upar, 1) || throw(BoundsError(upar))
if evolve_density && evolve_ppar
# this is the case where the density and parallel pressure are evolved
# separately from the normalized pdf, g_s = (√π f_s vth_s / n_s); the vpa
# coordinate is (dz/dt) / vth_s.
# Integrating calculates
# (upar_s / vth_s) = (1/√π)∫d(vpa/vth_s) * (vpa/vth_s) * (√π f_s vth_s / n_s)
# so convert from upar_s / vth_s to upar_s / c_s
@loop_r_z ir iz begin
vth = sqrt(2.0*ppar[iz,ir]/density[iz,ir])
upar[iz,ir] = integrate_over_vspace(@view(ff[:,:,iz,ir]),
vpa.grid, 1, vpa.wgts, vperp.grid, 0, vperp.wgts) * vth
end
elseif evolve_density
# corresponds to case where only the density is evolved separately from the
# normalised pdf, given by g_s = (√π f_s c_s / n_s); the vpa coordinate is
# (dz/dt) / c_s.
# Integrating calculates
# (upar_s / c_s) = (1/√π)∫d(vpa/c_s) * (vpa/c_s) * (√π f_s c_s / n_s)
@loop_r_z ir iz begin
upar[iz,ir] = integrate_over_vspace(@view(ff[:,:,iz,ir]),
vpa.grid, 1, vpa.wgts, vperp.grid, 0, vperp.wgts)
end
else
# When evolve_density = false, the evolved pdf is the 'true' pdf,
# and the vpa coordinate is (dz/dt) / c_s.
# Integrating calculates
# (n_s / N_e) * (upar_s / c_s) = (1/√π)∫d(vpa/c_s) * (vpa/c_s) * (√π f_s c_s / N_e)
@loop_r_z ir iz begin
upar[iz,ir] = integrate_over_vspace(@view(ff[:,:,iz,ir]),
vpa.grid, 1, vpa.wgts, vperp.grid, 0, vperp.wgts) /
density[iz,ir]
end
end
return nothing
end
"""
NB: if this function is called and if ppar_updated is false, then
the incoming pdf is the un-normalized pdf that satisfies int dv pdf = density
"""
function update_ppar!(ppar, ppar_updated, density, upar, pdf, vpa, vperp, z, r, composition,
evolve_density, evolve_upar)
@boundscheck composition.n_ion_species == size(ppar,3) || throw(BoundsError(ppar))
@boundscheck r.n == size(ppar,2) || throw(BoundsError(ppar))
@boundscheck z.n == size(ppar,1) || throw(BoundsError(ppar))
begin_s_r_z_region()
@loop_s is begin
if ppar_updated[is] == false
@views update_ppar_species!(ppar[:,:,is], density[:,:,is], upar[:,:,is],
pdf[:,:,:,:,is], vpa, vperp, z, r, evolve_density,
evolve_upar)
ppar_updated[is] = true
end
end
end
"""
calculate the updated energy density (or parallel pressure, ppar) for a given species;
which of these is calculated depends on the definition of the vpa coordinate
"""
function update_ppar_species!(ppar, density, upar, ff, vpa, vperp, z, r, evolve_density, evolve_upar)
@boundscheck vpa.n == size(ff, 1) || throw(BoundsError(ff))
@boundscheck vperp.n == size(ff, 2) || throw(BoundsError(ff))
@boundscheck z.n == size(ff, 3) || throw(BoundsError(ff))
@boundscheck r.n == size(ff, 4) || throw(BoundsError(ff))
@boundscheck z.n == size(ppar, 1) || throw(BoundsError(ppar))
@boundscheck r.n == size(ppar, 2) || throw(BoundsError(ppar))
if evolve_upar
# this is the case where the parallel flow and density are evolved separately
# from the normalized pdf, g_s = (√π f_s c_s / n_s); the vpa coordinate is
# ((dz/dt) - upar_s) / c_s>
# Integrating calculates (p_parallel/m_s n_s c_s^2) = (1/√π)∫d((vpa-upar_s)/c_s) (1/2)*((vpa-upar_s)/c_s)^2 * (√π f_s c_s / n_s)
# so convert from p_s / m_s n_s c_s^2 to ppar_s = p_s / m_s N_e c_s^2
@loop_r_z ir iz begin
ppar[iz,ir] = integrate_over_vspace(@view(ff[:,:,iz,ir]), vpa.grid, 2, vpa.wgts, vperp.grid, 0, vperp.wgts) *
density[iz,ir]
end
elseif evolve_density
# corresponds to case where only the density is evolved separately from the
# normalised pdf, given by g_s = (√π f_s c_s / n_s); the vpa coordinate is
# (dz/dt) / c_s.
# Integrating calculates
# (p_parallel/m_s n_s c_s^2) + (upar_s/c_s)^2 = (1/√π)∫d(vpa/c_s) (vpa/c_s)^2 * (√π f_s c_s / n_s)
# so subtract off the mean kinetic energy and multiply by density to get the
# internal energy density (aka pressure)
@loop_r_z ir iz begin
ppar[iz,ir] = (integrate_over_vspace(@view(ff[:,:,iz,ir]), vpa.grid, 2, vpa.wgts, vperp.grid, 0, vperp.wgts) -
upar[iz,ir]^2) * density[iz,ir]
end
else
# When evolve_density = false, the evolved pdf is the 'true' pdf,
# and the vpa coordinate is (dz/dt) / c_s.
# Integrating calculates
# (p_parallel/m_s N_e c_s^2) + (n_s/N_e)*(upar_s/c_s)^2 = (1/√π)∫d(vpa/c_s) (vpa/c_s)^2 * (√π f_s c_s / N_e)
# so subtract off the mean kinetic energy density to get the internal energy
# density (aka pressure)
@loop_r_z ir iz begin
ppar[iz,ir] = integrate_over_vspace(@view(ff[:,:,iz,ir]), vpa.grid, 2, vpa.wgts, vperp.grid, 0, vperp.wgts) -
density[iz,ir]*upar[iz,ir]^2
end
end
return nothing
end
"""
NB: the incoming pdf is the normalized pdf
"""
function update_qpar!(qpar, qpar_updated, density, upar, vth, pdf, vpa, vperp, z, r,
composition, evolve_density, evolve_upar, evolve_ppar)
@boundscheck composition.n_ion_species == size(qpar,3) || throw(BoundsError(qpar))
begin_s_r_z_region()
@loop_s is begin
if qpar_updated[is] == false
@views update_qpar_species!(qpar[:,:,is], density[:,:,is], upar[:,:,is],
vth[:,:,is], pdf[:,:,:,:,is], vpa, vperp, z, r,
evolve_density, evolve_upar, evolve_ppar)
qpar_updated[is] = true
end
end
end
"""
calculate the updated parallel heat flux (qpar) for a given species
"""
function update_qpar_species!(qpar, density, upar, vth, ff, vpa, vperp, z, r, evolve_density,
evolve_upar, evolve_ppar)
@boundscheck z.n == size(ff, 3) || throw(BoundsError(ff))
@boundscheck vperp.n == size(ff, 2) || throw(BoundsError(ff))
@boundscheck vpa.n == size(ff, 1) || throw(BoundsError(ff))
@boundscheck r.n == size(qpar, 2) || throw(BoundsError(qpar))
@boundscheck z.n == size(qpar, 1) || throw(BoundsError(qpar))
if evolve_upar && evolve_ppar
@loop_r_z ir iz begin
qpar[iz,ir] = integrate_over_vspace(@view(ff[:,:,iz,ir]), vpa.grid, 3, vpa.wgts, vperp.grid, 0, vperp.wgts) *
density[iz,ir] * vth[iz,ir]^3
end
elseif evolve_upar
@loop_r_z ir iz begin
qpar[iz,ir] = integrate_over_vspace(@view(ff[:,:,iz,ir]), vpa.grid, 3, vpa.wgts, vperp.grid, 0, vperp.wgts) *
density[iz,ir]
end
elseif evolve_ppar
@loop_r_z ir iz begin
@. vpa.scratch = vpa.grid - upar[iz,ir]
qpar[iz,ir] = integrate_over_vspace(@view(ff[:,:,iz,ir]), vpa.scratch, 3, vpa.wgts, vperp.grid, 0, vperp.wgts) *
density[iz,ir] * vth[iz,ir]^3
end
elseif evolve_density
@loop_r_z ir iz begin
@. vpa.scratch = vpa.grid - upar[iz,ir]
qpar[iz,ir] = integrate_over_vspace(@view(ff[:,:,iz,ir]), vpa.scratch, 3, vpa.wgts, vperp.grid, 0, vperp.wgts) *
density[iz,ir]
end
else
@loop_r_z ir iz begin
@. vpa.scratch = vpa.grid - upar[iz,ir]
qpar[iz,ir] = integrate_over_vspace(@view(ff[:,:,iz,ir]), vpa.scratch, 3, vpa.wgts, vperp.grid, 0, vperp.wgts)
end
end
return nothing
end
"""
runtime diagnostic routine for computing the Chodura ratio
in a single species plasma with Z = 1
"""
function update_chodura!(moments,ff,vpa,vperp,z,r,r_spectral,composition,geometry,scratch_dummy,z_advect)
@boundscheck composition.n_ion_species == size(ff, 5) || throw(BoundsError(ff))
begin_s_z_vperp_vpa_region()
dffdr = scratch_dummy.buffer_vpavperpzrs_1
ff_dummy = scratch_dummy.buffer_vpavperpzrs_2
if r.n > 1
# first compute d f / d r using centred reconciliation and place in dummy array #1
derivative_r!(dffdr, ff[:,:,:,:,:],
scratch_dummy.buffer_vpavperpzs_1, scratch_dummy.buffer_vpavperpzs_2,
scratch_dummy.buffer_vpavperpzs_3,scratch_dummy.buffer_vpavperpzs_4,
r_spectral,r)
else
@loop_s_r_z_vperp_vpa is ir iz ivperp ivpa begin
dffdr[ivpa,ivperp,iz,ir,is] = 0.0
end
end
del_vpa = minimum(vpa.grid[2:vpa.ngrid].-vpa.grid[1:vpa.ngrid-1])
begin_s_r_region()
if z.irank == 0
@loop_s_r is ir begin
@views moments.charged.chodura_integral_lower[ir,is] = update_chodura_integral_species!(ff[:,:,1,ir,is],dffdr[:,:,1,ir,is],
ff_dummy[:,:,1,ir,is],vpa,vperp,z,r,composition,geometry,z_advect[is].speed[1,:,:,ir],moments.charged.dens[1,ir,is],del_vpa)
end
else # we do not save this Chodura integral to the output file
@loop_s_r is ir begin
moments.charged.chodura_integral_lower[ir,is] = 0.0
end
end
if z.irank == z.nrank - 1
@loop_s_r is ir begin
@views moments.charged.chodura_integral_upper[ir,is] = update_chodura_integral_species!(ff[:,:,end,ir,is],dffdr[:,:,end,ir,is],
ff_dummy[:,:,end,ir,is],vpa,vperp,z,r,composition,geometry,z_advect[is].speed[end,:,:,ir],moments.charged.dens[end,ir,is],del_vpa)
end
else # we do not save this Chodura integral to the output file
@loop_s_r is ir begin
moments.charged.chodura_integral_upper[ir,is] = 0.0
end
end
end
"""
compute the integral needed for the generalised Chodura condition
IChodura = (Z^2 vBohm^2 / cref^2) * int ( f bz^2 / vz^2 + dfdr*rhostar/vz )
vBohm = sqrt(Z Te/mi)
with Z = 1 and mref = mi
cref = sqrt(2Ti/mi)
and normalise to the local ion density, appropriate to assessing the
Chodura condition
IChodura <= (Te/e)d ne / dphi |(sheath entrance) = ni
to a single species plasma with Z = 1
"""
function update_chodura_integral_species!(ff,dffdr,ff_dummy,vpa,vperp,z,r,composition,geometry,vz,dens,del_vpa)
@boundscheck vpa.n == size(ff, 1) || throw(BoundsError(ff))
@boundscheck vperp.n == size(ff, 2) || throw(BoundsError(ff))
@boundscheck vpa.n == size(dffdr, 1) || throw(BoundsError(dffdr))
@boundscheck vperp.n == size(dffdr, 2) || throw(BoundsError(dffdr))
@boundscheck vpa.n == size(ff_dummy, 1) || throw(BoundsError(ff_dummy))
@boundscheck vperp.n == size(ff_dummy, 2) || throw(BoundsError(ff_dummy))
bzed = geometry.bzed
@loop_vperp_vpa ivperp ivpa begin
# avoid divide by zero by making sure
# we are more than a vpa mimimum grid spacing away from
# the vz(vpa,r) = 0 velocity boundary
if abs(vz[ivpa,ivperp]) > 2.0*del_vpa
ff_dummy[ivpa,ivperp] = (ff[ivpa,ivperp]*bzed^2/(vz[ivpa,ivperp]^2) +
geometry.rhostar*dffdr[ivpa,ivperp]/vz[ivpa,ivperp])
else
ff_dummy[ivpa,ivperp] = 0.0
end
end
chodura_integral = integrate_over_vspace(@view(ff_dummy[:,:]), vpa.grid, 0, vpa.wgts, vperp.grid, 0, vperp.wgts)
# multiply by Te factor from vBohm and divide by the local ion density
chodura_integral *= 0.5*composition.T_e/dens
#println("chodura_integral: ",chodura_integral)
return chodura_integral
end
"""
Pre-calculate spatial derivatives of the moments that will be needed for the time advance
"""
function calculate_moment_derivatives!(moments, scratch, scratch_dummy, z, z_spectral,
numerical_dissipation)
begin_s_r_region()
@loop_s is begin
density = @view scratch.density[:,:,is]
upar = @view scratch.upar[:,:,is]
ppar = @view scratch.ppar[:,:,is]
qpar = @view moments.charged.qpar[:,:,is]
vth = @view moments.charged.vth[:,:,is]
dummy_zr = @view scratch_dummy.dummy_zrs[:,:,is]
buffer_r_1 = @view scratch_dummy.buffer_rs_1[:,is]
buffer_r_2 = @view scratch_dummy.buffer_rs_2[:,is]
buffer_r_3 = @view scratch_dummy.buffer_rs_3[:,is]
buffer_r_4 = @view scratch_dummy.buffer_rs_4[:,is]
buffer_r_5 = @view scratch_dummy.buffer_rs_5[:,is]
buffer_r_6 = @view scratch_dummy.buffer_rs_6[:,is]
if moments.evolve_density
# Upwinded using upar as advection velocity, to be used in continuity equation
@loop_r_z ir iz begin
dummy_zr[iz,ir] = -upar[iz,ir]
end
@views derivative_z!(moments.charged.ddens_dz_upwind[:,:,is], density,
dummy_zr, buffer_r_1, buffer_r_2, buffer_r_3, buffer_r_4,
buffer_r_5, buffer_r_6, z_spectral, z)
end
if moments.evolve_density &&
numerical_dissipation.moment_dissipation_coefficient > 0.0
# centred second derivative for dissipation
@views derivative_z!(dummy_zr, density, buffer_r_1, buffer_r_2, buffer_r_3,
buffer_r_4, z_spectral, z)
@views derivative_z!(moments.charged.d2dens_dz2[:,:,is], dummy_zr, buffer_r_1,
buffer_r_2, buffer_r_3, buffer_r_4, z_spectral, z)
end
if moments.evolve_density || moments.evolve_upar || moments.evolve_ppar
@views derivative_z!(moments.charged.dupar_dz[:,:,is], upar, buffer_r_1,
buffer_r_2, buffer_r_3, buffer_r_4, z_spectral, z)
end
if moments.evolve_upar
# Upwinded using upar as advection velocity, to be used in force-balance
# equation
@loop_r_z ir iz begin
dummy_zr[iz,ir] = -upar[iz,ir]
end
@views derivative_z!(moments.charged.dupar_dz_upwind[:,:,is], upar, dummy_zr,
buffer_r_1, buffer_r_2, buffer_r_3, buffer_r_4,
buffer_r_5, buffer_r_6, z_spectral, z)
end
if moments.evolve_upar &&
numerical_dissipation.moment_dissipation_coefficient > 0.0
# centred second derivative for dissipation
@views derivative_z!(dummy_zr, upar, buffer_r_1, buffer_r_2, buffer_r_3,
buffer_r_4, z_spectral, z)
@views derivative_z!(moments.charged.d2upar_dz2[:,:,is], dummy_zr, buffer_r_1,
buffer_r_2, buffer_r_3, buffer_r_4, z_spectral, z)
end
if moments.evolve_upar
@views derivative_z!(moments.charged.dppar_dz[:,:,is], ppar, buffer_r_1,
buffer_r_2, buffer_r_3, buffer_r_4, z_spectral, z)
end
if moments.evolve_ppar
# Upwinded using upar as advection velocity, to be used in energy equation
@loop_r_z ir iz begin
dummy_zr[iz,ir] = -upar[iz,ir]
end
@views derivative_z!(moments.charged.dppar_dz_upwind[:,:,is], ppar, dummy_zr,
buffer_r_1, buffer_r_2, buffer_r_3, buffer_r_4,
buffer_r_5, buffer_r_6, z_spectral, z)
# centred second derivative for dissipation
@views derivative_z!(dummy_zr, ppar, buffer_r_1, buffer_r_2, buffer_r_3,
buffer_r_4, z_spectral, z)
@views derivative_z!(moments.charged.d2ppar_dz2[:,:,is], dummy_zr, buffer_r_1,
buffer_r_2, buffer_r_3, buffer_r_4, z_spectral, z)
@views derivative_z!(moments.charged.dqpar_dz[:,:,is], qpar, buffer_r_1,
buffer_r_2, buffer_r_3, buffer_r_4, z_spectral, z)
@views derivative_z!(moments.charged.dvth_dz[:,:,is], vth, buffer_r_1,
buffer_r_2, buffer_r_3, buffer_r_4, z_spectral, z)
end
end
end
"""
update velocity moments of the evolved neutral pdf
"""
function update_moments_neutral!(moments, pdf, vz, vr, vzeta, z, r, composition)
begin_sn_r_z_region()
n_species = size(pdf,6)
@boundscheck n_species == size(moments.neutral.dens,3) || throw(BoundsError(moments))
@loop_sn isn begin
if moments.neutral.dens_updated[isn] == false
@views update_neutral_density_species!(moments.neutral.dens[:,:,isn],
pdf[:,:,:,:,:,isn], vz, vr, vzeta, z, r)
moments.neutral.dens_updated[isn] = true
end
if moments.neutral.uz_updated[isn] == false
# Can pass moments.neutral.pz here even though it has not been updated yet,
# because moments.neutral.pz isn only needed if evolve_ppar=true, in which
# case it will not be updated because it isn not calculated from the
# distribution function
@views update_neutral_uz_species!(moments.neutral.uz[:,:,isn],
moments.neutral.dens[:,:,isn],
moments.neutral.pz[:,:,isn],
pdf[:,:,:,:,:,isn], vz, vr, vzeta, z, r,
moments.evolve_density, moments.evolve_ppar)
moments.neutral.uz_updated[isn] = true
end
if moments.neutral.ur_updated[isn] == false
@views update_neutral_ur_species!(moments.neutral.ur[:,:,isn],
moments.neutral.dens[:,:,isn],
pdf[:,:,:,:,:,isn], vz, vr, vzeta, z, r)
moments.neutral.ur_updated[isn] = true
end
if moments.neutral.uzeta_updated[isn] == false
@views update_neutral_uzeta_species!(moments.neutral.uzeta[:,:,isn],
moments.neutral.dens[:,:,isn],
pdf[:,:,:,:,:,isn], vz, vr, vzeta, z, r)
moments.neutral.uzeta_updated[isn] = true
end
if moments.neutral.pz_updated[isn] == false
@views update_neutral_pz_species!(moments.neutral.pz[:,:,isn],
moments.neutral.dens[:,:,isn],
moments.neutral.uz[:,:,isn],
pdf[:,:,:,:,:,isn], vz, vr, vzeta, z, r,
moments.evolve_density, moments.evolve_upar)
moments.neutral.pz_updated[isn] = true
end
if moments.neutral.pr_updated[isn] == false
@views update_neutral_pr_species!(moments.neutral.pr[:,:,isn],
pdf[:,:,:,:,:,isn], vz, vr, vzeta, z, r)
moments.neutral.pr_updated[isn] = true
end
@loop_r_z ir iz begin
moments.neutral.vth[iz,ir,isn] =
sqrt(2*moments.neutral.pz[iz,ir,isn]/moments.neutral.dens[iz,ir,isn])
end
if moments.neutral.qz_updated[isn] == false
@views update_neutral_qz_species!(moments.neutral.qz[:,:,isn],
moments.neutral.dens[:,:,isn],
moments.neutral.uz[:,:,isn],
moments.neutral.vth[:,:,isn],
pdf[:,:,:,:,:,isn], vz, vr, vzeta, z, r,
moments.evolve_density, moments.evolve_upar,
moments.evolve_ppar)
moments.neutral.qz_updated[isn] = true
end
end
return nothing
end
"""
calculate the neutral density from the neutral pdf
"""
function update_neutral_density!(dens, dens_updated, pdf, vz, vr, vzeta, z, r,
composition)
begin_sn_r_z_region()
@boundscheck composition.n_neutral_species == size(pdf, 6) || throw(BoundsError(pdf))
@boundscheck composition.n_neutral_species == size(dens, 3) || throw(BoundsError(dens))
@loop_sn isn begin
if dens_updated[isn] == false
@views update_neutral_density_species!(dens[:,:,isn], pdf[:,:,:,:,:,isn], vz, vr, vzeta, z, r)
dens_updated[isn] = true
end
end
end
"""
calculate the updated density (dens) for a given species
"""
function update_neutral_density_species!(dens, ff, vz, vr, vzeta, z, r)
@boundscheck vz.n == size(ff, 1) || throw(BoundsError(ff))
@boundscheck vr.n == size(ff, 2) || throw(BoundsError(ff))
@boundscheck vzeta.n == size(ff, 3) || throw(BoundsError(ff))
@boundscheck z.n == size(ff, 4) || throw(BoundsError(ff))
@boundscheck r.n == size(ff, 5) || throw(BoundsError(ff))
@boundscheck z.n == size(dens, 1) || throw(BoundsError(dens))
@boundscheck r.n == size(dens, 2) || throw(BoundsError(dens))
@loop_r_z ir iz begin
dens[iz,ir] = integrate_over_neutral_vspace(@view(ff[:,:,:,iz,ir]),
vz.grid, 0, vz.wgts, vr.grid, 0, vr.wgts, vzeta.grid, 0, vzeta.wgts)
end
return nothing
end
function update_neutral_uz!(uz, uz_updated, density, pz, pdf, vz, vr, vzeta, z, r,
composition, evolve_density, evolve_ppar)
begin_sn_r_z_region()
@boundscheck composition.n_neutral_species == size(pdf, 6) || throw(BoundsError(pdf))
@boundscheck composition.n_neutral_species == size(uz, 3) || throw(BoundsError(uz))
@loop_sn isn begin
if uz_updated[isn] == false
@views update_neutral_uz_species!(uz[:,:,isn], density[:,:,isn], pz[:,:,isn],
pdf[:,:,:,:,:,isn], vz, vr, vzeta, z, r,
evolve_density, evolve_ppar)
uz_updated[isn] = true
end
end
end
"""
calculate the updated uz (mean velocity in z) for a given species
"""
function update_neutral_uz_species!(uz, density, pz, ff, vz, vr, vzeta, z, r,
evolve_density, evolve_ppar)
@boundscheck vz.n == size(ff, 1) || throw(BoundsError(ff))
@boundscheck vr.n == size(ff, 2) || throw(BoundsError(ff))
@boundscheck vzeta.n == size(ff, 3) || throw(BoundsError(ff))
@boundscheck z.n == size(ff, 4) || throw(BoundsError(ff))
@boundscheck r.n == size(ff, 5) || throw(BoundsError(ff))
@boundscheck z.n == size(uz, 1) || throw(BoundsError(uz))
@boundscheck r.n == size(uz, 2) || throw(BoundsError(uz))
if evolve_density && evolve_ppar
# this is the case where the density and parallel pressure are evolved
# separately from the normalized pdf, g_s = (√π f_s vth_s / n_s); the vz
# coordinate is (dz/dt) / vth_s.
# Integrating calculates
# (upar_s / vth_s) = (1/√π)∫d(vz/vth_s) * (vz/vth_s) * (√π f_s vth_s / n_s)
# so convert from upar_s / vth_s to upar_s / c_s
@loop_r_z ir iz begin
vth = sqrt(2.0*pz[iz,ir]/density[iz,ir])
uz[iz,ir] = integrate_over_neutral_vspace(@view(ff[:,:,:,iz,ir]),
vz.grid, 1, vz.wgts, vr.grid, 0, vr.wgts, vzeta.grid, 0,
vzeta.wgts) * vth
end
elseif evolve_density
# corresponds to case where only the density is evolved separately from the
# normalised pdf, given by g_s = (√π f_s c_s / n_s); the vz coordinate is
# (dz/dt) / c_s.
# Integrating calculates
# (upar_s / c_s) = (1/√π)∫d(vz/c_s) * (vz/c_s) * (√π f_s c_s / n_s)
@loop_r_z ir iz begin
uz[iz,ir] = integrate_over_neutral_vspace(@view(ff[:,:,:,iz,ir]),
vz.grid, 1, vz.wgts, vr.grid, 0, vr.wgts, vzeta.grid, 0,
vzeta.wgts)
end
else
# When evolve_density = false, the evolved pdf is the 'true' pdf,
# and the vz coordinate is (dz/dt) / c_s.
# Integrating calculates
# (n_s / N_e) * (uz / c_s) = (1/√π)∫d(vz/c_s) * (vz/c_s) * (√π f_s c_s / N_e)
@loop_r_z ir iz begin
uz[iz,ir] = integrate_over_neutral_vspace(@view(ff[:,:,:,iz,ir]),
vz.grid, 1, vz.wgts, vr.grid, 0, vr.wgts, vzeta.grid, 0,
vzeta.wgts) / density[iz,ir]
end
end
return nothing
end
function update_neutral_ur!(ur, ur_updated, density, pdf, vz, vr, vzeta, z, r,
composition)
begin_sn_r_z_region()
@boundscheck composition.n_neutral_species == size(pdf, 6) || throw(BoundsError(pdf))
@boundscheck composition.n_neutral_species == size(ur, 3) || throw(BoundsError(ur))
@loop_sn isn begin
if ur_updated[isn] == false
@views update_neutral_ur_species!(ur[:,:,isn], density[:,:,isn],
pdf[:,:,:,:,:,isn], vz, vr, vzeta, z, r)
ur_updated[isn] = true
end
end