diff --git a/.github/workflows/debug_checks.yml b/.github/workflows/debug_checks.yml index a4da80bc5..468547737 100644 --- a/.github/workflows/debug_checks.yml +++ b/.github/workflows/debug_checks.yml @@ -18,7 +18,7 @@ jobs: - uses: mpi4py/setup-mpi@v1 with: mpi: 'openmpi' - - uses: julia-actions/setup-julia@v1 + - uses: julia-actions/setup-julia@latest with: version: '1.10' arch: x64 diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index b563a2f0d..405ffcb9e 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -14,10 +14,9 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: julia-actions/setup-julia@v1 + - uses: julia-actions/setup-julia@latest with: version: '1.10' - arch: x64 - uses: julia-actions/cache@v1 - name: Test examples run: | diff --git a/.github/workflows/longtest.yml b/.github/workflows/longtest.yml index 9acc1f36b..80b9a9677 100644 --- a/.github/workflows/longtest.yml +++ b/.github/workflows/longtest.yml @@ -19,10 +19,9 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: julia-actions/setup-julia@v1 + - uses: julia-actions/setup-julia@latest with: version: '1.10' - arch: x64 - uses: julia-actions/cache@v1 - uses: julia-actions/julia-buildpkg@v1 with: diff --git a/.github/workflows/parallel_test.yml b/.github/workflows/parallel_test.yml index 30416a0f5..88fd99e63 100644 --- a/.github/workflows/parallel_test.yml +++ b/.github/workflows/parallel_test.yml @@ -17,7 +17,7 @@ jobs: - uses: mpi4py/setup-mpi@v1 with: mpi: 'openmpi' - - uses: julia-actions/setup-julia@v1 + - uses: julia-actions/setup-julia@latest with: version: '1.10' arch: x64 @@ -49,14 +49,14 @@ jobs: - uses: mpi4py/setup-mpi@v1 with: mpi: 'openmpi' - - uses: julia-actions/setup-julia@v1 + - uses: julia-actions/setup-julia@latest with: version: '1.10' - arch: x64 - uses: julia-actions/cache@v1 - run: | + MPILIBPATH=$(find /opt/homebrew/Cellar/open-mpi/ -name libmpi.dylib) touch Project.toml - julia --project -O3 --check-bounds=no -e 'import Pkg; Pkg.add(["MPI", "MPIPreferences"]); using MPIPreferences; MPIPreferences.use_system_binary()' + julia --project -O3 --check-bounds=no -e 'import Pkg; Pkg.add(["MPI", "MPIPreferences"]); using MPIPreferences; MPIPreferences.use_system_binary(library_names="/opt/homebrew/Cellar/open-mpi/5.0.3/lib/libmpi.dylib")' julia --project -O3 --check-bounds=no -e 'import Pkg; Pkg.add(["NCDatasets", "Random", "SpecialFunctions", "Test"]); Pkg.develop(path="moment_kinetics/")' julia --project -O3 --check-bounds=no -e 'import Pkg; Pkg.precompile()' # Need to use openmpi so that the following arguments work: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ccaca9dc0..edb0bffa2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,10 +18,9 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: julia-actions/setup-julia@v1 + - uses: julia-actions/setup-julia@latest with: version: '1.10' - arch: x64 - uses: julia-actions/cache@v1 - uses: julia-actions/julia-buildpkg@v1 with: diff --git a/.gitignore b/.gitignore index 4d3aedd9f..a6eaf3e33 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ makie_postproc.so plots_postproc.so precompile-temp post_processing_input.toml +.DS_Store diff --git a/docs/src/external_sources_notes.md b/docs/src/external_sources_notes.md index f805d1552..bdaadeffc 100644 --- a/docs/src/external_sources_notes.md +++ b/docs/src/external_sources_notes.md @@ -14,8 +14,8 @@ S_n &= A_n(r,z) \frac{1}{(\pi)^{3/2} (2 T_{\mathrm{source},n} / m_n)^{3/2}} \exp or in 1V simulations that do not include $v_\perp$, $v_\zeta$, $v_r$ dimensions ```math \begin{align} -S_i &= A_i(r,z) \frac{1}{sqrt{\pi} \sqrt{2 T_{\mathrm{source},i} / m_i}} \exp\left( -\frac{v_\perp^2}{T_{\mathrm{source},i}} \right) \\ -S_n &= A_n(r,z) \frac{1}{sqrt{\pi} \sqrt{2 T_{\mathrm{source},n} / m_n}} \exp\left( -\frac{v_z^2}{T_{\mathrm{source},n}} \right) +S_i &= A_i(r,z) \frac{1}{\sqrt{\pi} \sqrt{2 T_{\mathrm{source},i} / m_i}} \exp\left( -\frac{v_\perp^2}{T_{\mathrm{source},i}} \right) \\ +S_n &= A_n(r,z) \frac{1}{\sqrt{\pi} \sqrt{2 T_{\mathrm{source},n} / m_n}} \exp\left( -\frac{v_z^2}{T_{\mathrm{source},n}} \right) \end{align} ``` diff --git a/docs/src/moment_kinetic_equations.md b/docs/src/moment_kinetic_equations.md index c21735b04..5f5f81a40 100644 --- a/docs/src/moment_kinetic_equations.md +++ b/docs/src/moment_kinetic_equations.md @@ -1018,7 +1018,7 @@ and for neutrals where several of the ionization terms cancel & \qquad-\frac{w_{\|,n}}{2}\frac{1}{p_{\|,n}}\left(-\frac{\partial q_{\|,n}}{\partial z} - R_{in}\left(n_{i}p_{\|,n} - n_{n}p_{\|,i} - n_{n}n_{i}\left(u_{n} - u_{i}\right)^{2}\right) - + \int dv_\parallel S_{n} + u_{n}^2\int dv_\parallel v_\parallel^2 S_{n} + + \int dv_\parallel v_\parallel^2 S_{n} + u_{n}^2\int dv_\parallel S_{n} + v_{\mathrm{th},n}w_{\|,n}\frac{\partial p_{\|,n}}{\partial z}\right) \\ & \qquad\left. + \frac{w_{\parallel,n}}{2}\frac{1}{n_{n}}\int dv_\parallel S_{n} + \frac{w_{\|,n}^{2}}{2}\frac{v_{\mathrm{th},n}}{n_{n}}\frac{\partial n_{n}}{\partial z}\right]\frac{\partial g_{n}}{\partial w_{\|,n}} \\ diff --git a/examples/fokker-planck-1D2V/fokker-planck-1D2V-even_nz-shorttest-nstep200.toml b/examples/fokker-planck-1D2V/fokker-planck-1D2V-even_nz-shorttest-nstep200.toml index 1b8758f47..eb7d60b18 100644 --- a/examples/fokker-planck-1D2V/fokker-planck-1D2V-even_nz-shorttest-nstep200.toml +++ b/examples/fokker-planck-1D2V/fokker-planck-1D2V-even_nz-shorttest-nstep200.toml @@ -86,7 +86,7 @@ vperp_discretization = "gausslegendre_pseudospectral" #vzeta_bc = "periodic" #vzeta_discretization = "chebyshev_pseudospectral" -#[numerical_dissipation] +#[ion_numerical_dissipation] #vpa_dissipation_coefficient = 0.0 #vperp_dissipation_coefficient = 0.0 #z_dissipation_coefficient = 0.1 diff --git a/examples/geometry/1D-mirror.toml b/examples/geometry/1D-mirror.toml index 66c36fd5b..eb484f49d 100644 --- a/examples/geometry/1D-mirror.toml +++ b/examples/geometry/1D-mirror.toml @@ -71,7 +71,7 @@ vz_L = 18.0 vz_bc = "both_zero" vz_discretization = "chebyshev_pseudospectral" -[numerical_dissipation] +[ion_numerical_dissipation] vpa_dissipation_coefficient = 1.0e-3 #1.0e-2 #1.0e-1 vperp_dissipation_coefficient = 1.0e-3 #1.0e-2 #1.0e-1 force_minimum_pdf_value = 0.0 diff --git a/examples/gk-ions/2D-periodic-gk.toml b/examples/gk-ions/2D-periodic-gk.toml index df3f04e7d..36a8eacb3 100644 --- a/examples/gk-ions/2D-periodic-gk.toml +++ b/examples/gk-ions/2D-periodic-gk.toml @@ -73,15 +73,19 @@ vz_L = 18.0 vz_bc = "both_zero" vz_discretization = "chebyshev_pseudospectral" -[numerical_dissipation] -vpa_dissipation_coefficient = 1.0e-3 #1.0e-2 #1.0e-1 -vperp_dissipation_coefficient = 1.0e-3 #1.0e-2 #1.0e-1 -#r_disspipation_coefficient = 1.0e-3 -#force_minimum_pdf_value = 0.0 - [geometry] #option="1D-mirror" DeltaB=0.0 option="constant-helical" pitch=0.1 rhostar= 0.1 + +[ion_numerical_dissipation] +vpa_dissipation_coefficient = 1.0e-3 #1.0e-2 #1.0e-1 +vperp_dissipation_coefficient = 1.0e-3 #1.0e-2 #1.0e-1 +#r_disspipation_coefficient = 1.0e-3 +#force_minimum_pdf_value = 0.0 + +[neutral_numerical_dissipation] +moment_dissipation_coefficient = 0.0001 +vz_dissipation_coefficient = 0.01 \ No newline at end of file diff --git a/examples/numerical-dissipation/num-diss-relaxation.toml b/examples/numerical-dissipation/num-diss-relaxation.toml index e196c2e84..6fb8b0086 100644 --- a/examples/numerical-dissipation/num-diss-relaxation.toml +++ b/examples/numerical-dissipation/num-diss-relaxation.toml @@ -58,7 +58,7 @@ nwrite = 2000 nwrite_dfns = 2000 split_operators = false -[numerical_dissipation] +[ion_numerical_dissipation] vpa_dissipation_coefficient = 0.1 vperp_dissipation_coefficient = 0.1 z_dissipation_coefficient = -1.0 diff --git a/examples/recycling-fraction/wall-bc_recyclefraction0.5_split3-init.toml b/examples/recycling-fraction/wall-bc_recyclefraction0.5_split3-init.toml index 9a0e2120d..5efe4c239 100644 --- a/examples/recycling-fraction/wall-bc_recyclefraction0.5_split3-init.toml +++ b/examples/recycling-fraction/wall-bc_recyclefraction0.5_split3-init.toml @@ -81,8 +81,14 @@ z_width = 0.125 source_strength = 2.0 source_T = 2.0 -[numerical_dissipation] +[ion_numerical_dissipation] vpa_dissipation_coefficient = 1.0e-1 #vpa_dissipation_coefficient = 1.0e-2 #vpa_dissipation_coefficient = 1.0e-3 force_minimum_pdf_value = 0.0 + +[neutral_numerical_dissipation] +vz_dissipation_coefficient = 1.0e-1 +#vz_dissipation_coefficient = 1.0e-2 +#vz_dissipation_coefficient = 1.0e-3 +force_minimum_pdf_value = 0.0 diff --git a/examples/recycling-fraction/wall-bc_recyclefraction0.5_split3.toml b/examples/recycling-fraction/wall-bc_recyclefraction0.5_split3.toml index 7b74635ff..4436f7f8d 100644 --- a/examples/recycling-fraction/wall-bc_recyclefraction0.5_split3.toml +++ b/examples/recycling-fraction/wall-bc_recyclefraction0.5_split3.toml @@ -81,8 +81,14 @@ z_width = 0.125 source_strength = 2.0 source_T = 2.0 -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 1.0e-1 #vpa_dissipation_coefficient = 1.0e-2 #vpa_dissipation_coefficient = 1.0e-3 force_minimum_pdf_value = 0.0 + +[neutral_numerical_dissipation] +#vz_dissipation_coefficient = 1.0e-1 +#vz_dissipation_coefficient = 1.0e-2 +#vz_dissipation_coefficient = 1.0e-3 +force_minimum_pdf_value = 0.0 diff --git a/examples/recycling-fraction/wall-bc_recyclefraction0.5_split3_fekete104.toml b/examples/recycling-fraction/wall-bc_recyclefraction0.5_split3_fekete104.toml index 2719cc899..2843b91f8 100644 --- a/examples/recycling-fraction/wall-bc_recyclefraction0.5_split3_fekete104.toml +++ b/examples/recycling-fraction/wall-bc_recyclefraction0.5_split3_fekete104.toml @@ -87,8 +87,14 @@ z_width = 0.125 source_strength = 2.0 source_T = 2.0 -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 1.0e-1 #vpa_dissipation_coefficient = 1.0e-2 #vpa_dissipation_coefficient = 1.0e-3 force_minimum_pdf_value = 0.0 + +[neutral_numerical_dissipation] +#vz_dissipation_coefficient = 1.0e-1 +#vz_dissipation_coefficient = 1.0e-2 +#vz_dissipation_coefficient = 1.0e-3 +force_minimum_pdf_value = 0.0 diff --git a/examples/recycling-fraction/wall-bc_recyclefraction0.5_split3_fekete43.toml b/examples/recycling-fraction/wall-bc_recyclefraction0.5_split3_fekete43.toml index ac78baeb3..82c8e1ff1 100644 --- a/examples/recycling-fraction/wall-bc_recyclefraction0.5_split3_fekete43.toml +++ b/examples/recycling-fraction/wall-bc_recyclefraction0.5_split3_fekete43.toml @@ -87,8 +87,14 @@ z_width = 0.125 source_strength = 2.0 source_T = 2.0 -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 1.0e-1 #vpa_dissipation_coefficient = 1.0e-2 #vpa_dissipation_coefficient = 1.0e-3 force_minimum_pdf_value = 0.0 + +[neutral_numerical_dissipation] +#vz_dissipation_coefficient = 1.0e-1 +#vz_dissipation_coefficient = 1.0e-2 +#vz_dissipation_coefficient = 1.0e-3 +force_minimum_pdf_value = 0.0 diff --git a/examples/recycling-fraction/wall-bc_recyclefraction0.5_split3_fekete64.toml b/examples/recycling-fraction/wall-bc_recyclefraction0.5_split3_fekete64.toml index 3c9c289dc..8753de127 100644 --- a/examples/recycling-fraction/wall-bc_recyclefraction0.5_split3_fekete64.toml +++ b/examples/recycling-fraction/wall-bc_recyclefraction0.5_split3_fekete64.toml @@ -88,7 +88,7 @@ source_strength = 2.0 source_T = 2.0 [numerical_dissipation] -#vpa_dissipation_coefficient = 1.0e-1 -#vpa_dissipation_coefficient = 1.0e-2 -#vpa_dissipation_coefficient = 1.0e-3 +#vz_dissipation_coefficient = 1.0e-1 +#vz_dissipation_coefficient = 1.0e-2 +#vz_dissipation_coefficient = 1.0e-3 force_minimum_pdf_value = 0.0 diff --git a/examples/wall-bc/wall-bc_cheb.toml b/examples/wall-bc/wall-bc_cheb.toml index cd0dce518..4a076b110 100644 --- a/examples/wall-bc/wall-bc_cheb.toml +++ b/examples/wall-bc/wall-bc_cheb.toml @@ -65,6 +65,10 @@ dt = 1.0e-5 nwrite = 1000 split_operators = false -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 1.0e-3 #1.0e-2 #1.0e-1 force_minimum_pdf_value = 0.0 + +[neutral_numerical_dissipation] +#vz_dissipation_coefficient = 1.0e-3 #1.0e-2 #1.0e-1 +force_minimum_pdf_value = 0.0 \ No newline at end of file diff --git a/examples/wall-bc/wall-bc_cheb_split1.toml b/examples/wall-bc/wall-bc_cheb_split1.toml index e995fd5f1..4c57ebf48 100644 --- a/examples/wall-bc/wall-bc_cheb_split1.toml +++ b/examples/wall-bc/wall-bc_cheb_split1.toml @@ -66,6 +66,10 @@ dt = 1.0e-5 nwrite = 1000 split_operators = false -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 1.0e-3 #1.0e-2 #1.0e-1 force_minimum_pdf_value = 0.0 + +[neutral_numerical_dissipation] +#vz_dissipation_coefficient = 1.0e-3 #1.0e-2 #1.0e-1 +force_minimum_pdf_value = 0.0 \ No newline at end of file diff --git a/examples/wall-bc/wall-bc_cheb_split2.toml b/examples/wall-bc/wall-bc_cheb_split2.toml index 32ca888ff..53f29f0ac 100644 --- a/examples/wall-bc/wall-bc_cheb_split2.toml +++ b/examples/wall-bc/wall-bc_cheb_split2.toml @@ -66,6 +66,10 @@ dt = 1.0e-5 nwrite = 1000 split_operators = false -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 1.0e-3 #1.0e-2 #1.0e-1 force_minimum_pdf_value = 0.0 + +[neutral_numerical_dissipation] +#vz_dissipation_coefficient = 1.0e-3 #1.0e-2 #1.0e-1 +force_minimum_pdf_value = 0.0 \ No newline at end of file diff --git a/examples/wall-bc/wall-bc_cheb_split3.toml b/examples/wall-bc/wall-bc_cheb_split3.toml index b400adb5a..3db98a2da 100644 --- a/examples/wall-bc/wall-bc_cheb_split3.toml +++ b/examples/wall-bc/wall-bc_cheb_split3.toml @@ -66,6 +66,10 @@ dt = 1.0e-5 nwrite = 1000 split_operators = false -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 1.0e-3 #1.0e-2 #1.0e-1 force_minimum_pdf_value = 0.0 + +[neutral_numerical_dissipation] +#vz_dissipation_coefficient = 1.0e-3 #1.0e-2 #1.0e-1 +force_minimum_pdf_value = 0.0 \ No newline at end of file diff --git a/examples/wall-bc/wall-bc_no-neutrals.toml b/examples/wall-bc/wall-bc_no-neutrals.toml index 267247630..ce5da5ba8 100644 --- a/examples/wall-bc/wall-bc_no-neutrals.toml +++ b/examples/wall-bc/wall-bc_no-neutrals.toml @@ -77,7 +77,7 @@ z_width = 0.125 source_strength = 8.0 source_T = 1.0 -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 1.0e-1 #vpa_dissipation_coefficient = 1.0e-2 #vpa_dissipation_coefficient = 1.0e-3 diff --git a/examples/wall-bc/wall-bc_no-neutrals_split1.toml b/examples/wall-bc/wall-bc_no-neutrals_split1.toml index 1350ba3dc..cae94a045 100644 --- a/examples/wall-bc/wall-bc_no-neutrals_split1.toml +++ b/examples/wall-bc/wall-bc_no-neutrals_split1.toml @@ -77,7 +77,7 @@ z_width = 0.125 source_strength = 8.0 source_T = 1.0 -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 1.0e-1 #vpa_dissipation_coefficient = 1.0e-2 #vpa_dissipation_coefficient = 1.0e-3 diff --git a/examples/wall-bc/wall-bc_no-neutrals_split2.toml b/examples/wall-bc/wall-bc_no-neutrals_split2.toml index fb63806b1..ce74ec368 100644 --- a/examples/wall-bc/wall-bc_no-neutrals_split2.toml +++ b/examples/wall-bc/wall-bc_no-neutrals_split2.toml @@ -77,7 +77,7 @@ z_width = 0.125 source_strength = 8.0 source_T = 1.0 -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 1.0e-1 #vpa_dissipation_coefficient = 1.0e-2 #vpa_dissipation_coefficient = 1.0e-3 diff --git a/examples/wall-bc/wall-bc_no-neutrals_split3.toml b/examples/wall-bc/wall-bc_no-neutrals_split3.toml index bb1b29b69..23316e611 100644 --- a/examples/wall-bc/wall-bc_no-neutrals_split3.toml +++ b/examples/wall-bc/wall-bc_no-neutrals_split3.toml @@ -77,7 +77,7 @@ z_width = 0.125 source_strength = 8.0 source_T = 1.0 -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 1.0e-1 #vpa_dissipation_coefficient = 1.0e-2 #vpa_dissipation_coefficient = 1.0e-3 diff --git a/examples/wall-bc/wall-bc_volumerecycle.toml b/examples/wall-bc/wall-bc_volumerecycle.toml index bd1d59cc3..1cbf7f1b2 100644 --- a/examples/wall-bc/wall-bc_volumerecycle.toml +++ b/examples/wall-bc/wall-bc_volumerecycle.toml @@ -84,12 +84,18 @@ active = true source_type = "recycling" recycling_controller_fraction = 0.25 -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 1.0e-1 #vpa_dissipation_coefficient = 1.0e-2 #vpa_dissipation_coefficient = 1.0e-3 #force_minimum_pdf_value = 0.0 +[neutral_numerical_dissipation] +#vz_dissipation_coefficient = 1.0e-1 +#vz_dissipation_coefficient = 1.0e-2 +#vz_dissipation_coefficient = 1.0e-3 +force_minimum_pdf_value = 0.0 + [krook_collisions] use_krook = true frequency_option = "reference_parameters" \ No newline at end of file diff --git a/examples/wall-bc/wall-bc_volumerecycle_split1.toml b/examples/wall-bc/wall-bc_volumerecycle_split1.toml index c81531273..442ce033d 100644 --- a/examples/wall-bc/wall-bc_volumerecycle_split1.toml +++ b/examples/wall-bc/wall-bc_volumerecycle_split1.toml @@ -84,12 +84,18 @@ active = true source_type = "recycling" recycling_controller_fraction = 0.25 -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 1.0e-1 #vpa_dissipation_coefficient = 1.0e-2 #vpa_dissipation_coefficient = 1.0e-3 #force_minimum_pdf_value = 0.0 +[neutral_numerical_dissipation] +#vz_dissipation_coefficient = 1.0e-1 +#vz_dissipation_coefficient = 1.0e-2 +#vz_dissipation_coefficient = 1.0e-3 +#force_minimum_pdf_value = 0.0 + [krook_collisions] use_krook = true frequency_option = "reference_parameters" \ No newline at end of file diff --git a/examples/wall-bc/wall-bc_volumerecycle_split2.toml b/examples/wall-bc/wall-bc_volumerecycle_split2.toml index 24fb262f9..08eb2ed6c 100644 --- a/examples/wall-bc/wall-bc_volumerecycle_split2.toml +++ b/examples/wall-bc/wall-bc_volumerecycle_split2.toml @@ -84,12 +84,18 @@ active = true source_type = "recycling" recycling_controller_fraction = 0.25 -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 1.0e-1 #vpa_dissipation_coefficient = 1.0e-2 #vpa_dissipation_coefficient = 1.0e-3 #force_minimum_pdf_value = 0.0 +[neutral_numerical_dissipation] +#vz_dissipation_coefficient = 1.0e-1 +#vz_dissipation_coefficient = 1.0e-2 +#vz_dissipation_coefficient = 1.0e-3 +#force_minimum_pdf_value = 0.0 + [krook_collisions] use_krook = true frequency_option = "reference_parameters" \ No newline at end of file diff --git a/examples/wall-bc/wall-bc_volumerecycle_split3.toml b/examples/wall-bc/wall-bc_volumerecycle_split3.toml index 3441d6a4f..0c8dad98d 100644 --- a/examples/wall-bc/wall-bc_volumerecycle_split3.toml +++ b/examples/wall-bc/wall-bc_volumerecycle_split3.toml @@ -84,12 +84,18 @@ active = true source_type = "recycling" recycling_controller_fraction = 0.25 -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 1.0e-1 #vpa_dissipation_coefficient = 1.0e-2 #vpa_dissipation_coefficient = 1.0e-3 force_minimum_pdf_value = 0.0 +[neutral_numerical_dissipation] +#vz_dissipation_coefficient = 1.0e-1 +#vz_dissipation_coefficient = 1.0e-2 +#vz_dissipation_coefficient = 1.0e-3 +force_minimum_pdf_value = 0.0 + [krook_collisions] use_krook = true frequency_option = "reference_parameters" \ No newline at end of file diff --git a/machines/marconi/julia.env b/machines/marconi/julia.env index 43b24569e..dfb006696 100644 --- a/machines/marconi/julia.env +++ b/machines/marconi/julia.env @@ -2,7 +2,7 @@ module purge module load env-skl profile/base profile/advanced -module load gnu/7.3.0 openmpi/3.1.4--gnu--7.3.0 intel/pe-xe-2018--binary python/3.9.4 +module load gnu/7.3.0 openmpi/3.1.4--gnu--7.3.0 intel/pe-xe-2018--binary python/3.9.4 git/2.17 # Needed because Julia's Cairo library complains about libz<1.2.9 module load zlib/1.2.11--intel--pe-xe-2018--binary diff --git a/makie_post_processing/makie_post_processing/src/makie_post_processing.jl b/makie_post_processing/makie_post_processing/src/makie_post_processing.jl index 31376995f..725a16e86 100644 --- a/makie_post_processing/makie_post_processing/src/makie_post_processing.jl +++ b/makie_post_processing/makie_post_processing/src/makie_post_processing.jl @@ -30,10 +30,7 @@ using moment_kinetics.analysis: analyze_fields_data, check_Chodura_condition, get_unnormalised_f_2d using moment_kinetics.array_allocation: allocate_float using moment_kinetics.coordinates: define_coordinate -using moment_kinetics.input_structs: grid_input, advection_input, - set_defaults_and_check_top_level!, - set_defaults_and_check_section!, Dict_to_NamedTuple -using moment_kinetics.krook_collisions: get_collision_frequency +using moment_kinetics.input_structs using moment_kinetics.looping: all_dimensions, ion_dimensions, neutral_dimensions using moment_kinetics.manufactured_solns: manufactured_solutions, manufactured_electric_fields @@ -303,11 +300,13 @@ function makie_post_process(run_dir::Union{String,Tuple}, end end - plot_charged_pdf_2D_at_wall(run_info_dfns; plot_prefix=plot_prefix) + plot_ion_pdf_2D_at_wall(run_info_dfns; plot_prefix=plot_prefix) if has_neutrals plot_neutral_pdf_2D_at_wall(run_info_dfns; plot_prefix=plot_prefix) end + constraints_plots(run_info; plot_prefix=plot_prefix) + if has_rdim # Plots for 2D instability do not make sense for 1D simulations instability_input = input_dict["instability2D"] @@ -541,11 +540,36 @@ function _setup_single_input!(this_input_dict::OrderedDict{String,Any}, nz_min = 1 end if dfns && has_run_info - nvperp_min = minimum(ri.vperp.n for ri in run_info if ri !== nothing) - nvpa_min = minimum(ri.vpa.n for ri in run_info if ri !== nothing) - nvzeta_min = minimum(ri.vzeta.n for ri in run_info if ri !== nothing) - nvr_min = minimum(ri.vr.n for ri in run_info if ri !== nothing) - nvz_min = minimum(ri.vz.n for ri in run_info if ri !== nothing) + if any(ri.vperp !== nothing for ri ∈ run_info) + nvperp_min = minimum(ri.vperp.n for ri in run_info + if ri !== nothing && ri.vperp !== nothing) + else + nvperp_min = 1 + end + if any(ri.vpa !== nothing for ri ∈ run_info) + nvpa_min = minimum(ri.vpa.n for ri in run_info + if ri !== nothing && ri.vpa !== nothing) + else + nvpa_min = 1 + end + if any(ri.vzeta !== nothing for ri ∈ run_info) + nvzeta_min = minimum(ri.vzeta.n for ri in run_info + if ri !== nothing && ri.vzeta !== nothing) + else + nvzeta_min = 1 + end + if any(ri.vr !== nothing for ri ∈ run_info) + nvr_min = minimum(ri.vr.n for ri in run_info + if ri !== nothing && ri.vr !== nothing) + else + nvr_min = 1 + end + if any(ri.vz !== nothing for ri ∈ run_info) + nvz_min = minimum(ri.vz.n for ri in run_info + if ri !== nothing && ri.vz !== nothing) + else + nvz_min = 1 + end else nvperp_min = 1 nvpa_min = 1 @@ -684,6 +708,22 @@ function _setup_single_input!(this_input_dict::OrderedDict{String,Any}, animation_ext=this_input_dict["animation_ext"], ) + set_defaults_and_check_section!( + this_input_dict, "constraints"; + plot=false, + animate=false, + it0=this_input_dict["it0"], + ir0=this_input_dict["ir0"], + iz0=this_input_dict["iz0"], + ivperp0=this_input_dict["ivperp0"], + ivpa0=this_input_dict["ivpa0"], + ivzeta0=this_input_dict["ivzeta0"], + ivr0=this_input_dict["ivr0"], + ivz0=this_input_dict["ivz0"], + animation_ext=this_input_dict["animation_ext"], + show_element_boundaries=this_input_dict["show_element_boundaries"], + ) + set_defaults_and_check_section!( this_input_dict, "Chodura_condition"; plot_vs_t=false, @@ -1263,7 +1303,7 @@ end for dim ∈ one_dimension_combinations function_name_str = "plot_vs_$dim" function_name = Symbol(function_name_str) - spaces = " " ^ length(function_name_str) + spaces = " " ^ (length(function_name_str) + 1) dim_str = String(dim) if dim == :t dim_grid = :( run_info.time ) @@ -1275,17 +1315,17 @@ for dim ∈ one_dimension_combinations export $function_name """ - function $($function_name_str)(run_info::Tuple, var_name; is=1, data=nothing, - $($spaces)input=nothing, outfile=nothing, yscale=nothing, - transform=identity, axis_args=Dict{Symbol,Any}(), it=nothing, - $($spaces)ir=nothing, iz=nothing, ivperp=nothing, ivpa=nothing, - $($spaces)ivzeta=nothing, ivr=nothing, ivz=nothing, kwargs...) - function $($function_name_str)(run_info, var_name; is=1, data=nothing, - $($spaces)input=nothing, ax=nothing, label=nothing, - $($spaces)outfile=nothing, yscale=nothing, transform=identity, - $($spaces)axis_args=Dict{Symbol,Any}(), it=nothing, ir=nothing, - $($spaces)iz=nothing, ivperp=nothing, ivpa=nothing, ivzeta=nothing, - $($spaces)ivr=nothing, ivz=nothing, kwargs...) + $($function_name_str)(run_info::Tuple, var_name; is=1, data=nothing, + $($spaces)input=nothing, outfile=nothing, yscale=nothing, + transform=identity, axis_args=Dict{Symbol,Any}(), it=nothing, + $($spaces)ir=nothing, iz=nothing, ivperp=nothing, ivpa=nothing, + $($spaces)ivzeta=nothing, ivr=nothing, ivz=nothing, kwargs...) + $($function_name_str)(run_info, var_name; is=1, data=nothing, + $($spaces)input=nothing, ax=nothing, label=nothing, + $($spaces)outfile=nothing, yscale=nothing, transform=identity, + $($spaces)axis_args=Dict{Symbol,Any}(), it=nothing, ir=nothing, + $($spaces)iz=nothing, ivperp=nothing, ivpa=nothing, ivzeta=nothing, + $($spaces)ivr=nothing, ivz=nothing, kwargs...) Plot `var_name` from the run(s) represented by `run_info` (as returned by [`get_run_info`](@ref)) vs $($dim_str). @@ -1350,7 +1390,7 @@ for dim ∈ one_dimension_combinations if input === nothing if run_info[1].dfns if var_name ∈ keys(input_dict_dfns) - input = input_dict[var_name] + input = input_dict_dfns[var_name] else input = input_dict_dfns end @@ -1412,7 +1452,7 @@ for dim ∈ one_dimension_combinations if input === nothing if run_info.dfns if var_name ∈ keys(input_dict_dfns) - input = input_dict[var_name] + input = input_dict_dfns[var_name] else input = input_dict_dfns end @@ -1481,7 +1521,7 @@ end for (dim1, dim2) ∈ two_dimension_combinations function_name_str = "plot_vs_$(dim2)_$(dim1)" function_name = Symbol(function_name_str) - spaces = " " ^ length(function_name_str) + spaces = " " ^ (length(function_name_str) + 1) dim1_str = String(dim1) dim2_str = String(dim2) if dim1 == :t @@ -1496,19 +1536,19 @@ for (dim1, dim2) ∈ two_dimension_combinations export $function_name """ - function $($function_name_str)(run_info::Tuple, var_name; is=1, data=nothing, - $($spaces)input=nothing, outfile=nothing, colorscale=identity, - $($spaces)transform=identity, axis_args=Dict{Symbol,Any}(), - $($spaces)it=nothing, ir=nothing, iz=nothing, ivperp=nothing, - $($spaces)ivpa=nothing, ivzeta=nothing, ivr=nothing, ivz=nothing, - $($spaces)kwargs...) - function $($function_name_str)(run_info, var_name; is=1, data=nothing, - $($spaces)input=nothing, ax=nothing, - $($spaces)colorbar_place=nothing, title=nothing, - $($spaces)outfile=nothing, colorscale=identity, transform=identity, - $($spaces)axis_args=Dict{Symbol,Any}(), it=nothing, ir=nothing, - $($spaces)iz=nothing, ivperp=nothing, ivpa=nothing, ivzeta=nothing, - $($spaces)ivr=nothing, ivz=nothing, kwargs...) + $($function_name_str)(run_info::Tuple, var_name; is=1, data=nothing, + $($spaces)input=nothing, outfile=nothing, colorscale=identity, + $($spaces)transform=identity, axis_args=Dict{Symbol,Any}(), + $($spaces)it=nothing, ir=nothing, iz=nothing, ivperp=nothing, + $($spaces)ivpa=nothing, ivzeta=nothing, ivr=nothing, ivz=nothing, + $($spaces)kwargs...) + $($function_name_str)(run_info, var_name; is=1, data=nothing, + $($spaces)input=nothing, ax=nothing, + $($spaces)colorbar_place=nothing, title=nothing, + $($spaces)outfile=nothing, colorscale=identity, transform=identity, + $($spaces)axis_args=Dict{Symbol,Any}(), it=nothing, ir=nothing, + $($spaces)iz=nothing, ivperp=nothing, ivpa=nothing, ivzeta=nothing, + $($spaces)ivr=nothing, ivz=nothing, kwargs...) Plot `var_name` from the run(s) represented by `run_info` (as returned by [`get_run_info`](@ref))vs $($dim1_str) and $($dim2_str). @@ -1597,7 +1637,7 @@ for (dim1, dim2) ∈ two_dimension_combinations if input === nothing if run_info.dfns if var_name ∈ keys(input_dict_dfns) - input = input_dict[var_name] + input = input_dict_dfns[var_name] else input = input_dict_dfns end @@ -1688,7 +1728,7 @@ end for dim ∈ one_dimension_combinations_no_t function_name_str = "animate_vs_$dim" function_name = Symbol(function_name_str) - spaces = " " ^ length(function_name_str) + spaces = " " ^ (length(function_name_str) + 1) dim_str = String(dim) dim_grid = :( run_info.$dim.grid ) idim = Symbol(:i, dim) @@ -1696,19 +1736,19 @@ for dim ∈ one_dimension_combinations_no_t export $function_name """ - function $($function_name_str)(run_info::Tuple, var_name; is=1, data=nothing, - $($spaces)input=nothing, outfile=nothing, yscale=nothing, - $($spaces)transform=identity, ylims=nothing, - $($spaces)axis_args=Dict{Symbol,Any}(), it=nothing, ir=nothing, iz=nothing, - $($spaces)ivperp=nothing, ivpa=nothing, ivzeta=nothing, ivr=nothing, - $($spaces)ivz=nothing, kwargs...) - function $($function_name_str)(run_info, var_name; is=1, data=nothing, - $($spaces)input=nothing, frame_index=nothing, ax=nothing, - $($spaces)fig=nothing, outfile=nothing, yscale=nothing, - $($spaces)transform=identity, ylims=nothing, - $($spaces)axis_args=Dict{Symbol,Any}(), it=nothing, ir=nothing, iz=nothing, - $($spaces)ivperp=nothing, ivpa=nothing, ivzeta=nothing, ivr=nothing, - $($spaces)ivz=nothing, kwargs...) + $($function_name_str)(run_info::Tuple, var_name; is=1, data=nothing, + $($spaces)input=nothing, outfile=nothing, yscale=nothing, + $($spaces)transform=identity, ylims=nothing, + $($spaces)axis_args=Dict{Symbol,Any}(), it=nothing, ir=nothing, iz=nothing, + $($spaces)ivperp=nothing, ivpa=nothing, ivzeta=nothing, ivr=nothing, + $($spaces)ivz=nothing, kwargs...) + $($function_name_str)(run_info, var_name; is=1, data=nothing, + $($spaces)input=nothing, frame_index=nothing, ax=nothing, + $($spaces)fig=nothing, outfile=nothing, yscale=nothing, + $($spaces)transform=identity, ylims=nothing, label=nothing, + $($spaces)axis_args=Dict{Symbol,Any}(), it=nothing, ir=nothing, iz=nothing, + $($spaces)ivperp=nothing, ivpa=nothing, ivzeta=nothing, ivr=nothing, + $($spaces)ivz=nothing, kwargs...) Animate `var_name` from the run(s) represented by `run_info` (as returned by [`get_run_info`](@ref))vs $($dim_str). @@ -1740,6 +1780,9 @@ for dim ∈ one_dimension_combinations_no_t When a single `run_info` is passed, an `Axis` can be passed to `ax`. If it is, the plot will be added to `ax`. + When a single `run_info` is passed, `label` can be passed to set a custom + label for the line. By default the `run_info.run_name` is used. + `outfile` is required for animations unless `ax` is passed. The animation will be saved to a file named `outfile`. The suffix determines the file type. If both `outfile` and `ax` are passed, then the `Figure` containing @@ -1777,7 +1820,7 @@ for dim ∈ one_dimension_combinations_no_t if input === nothing if run_info[1].dfns if var_name ∈ keys(input_dict_dfns) - input = input_dict[var_name] + input = input_dict_dfns[var_name] else input = input_dict_dfns end @@ -1801,9 +1844,11 @@ for dim ∈ one_dimension_combinations_no_t all(isapprox.(ri.time, run_info[1].time)) for ri ∈ run_info[2:end]) # All times are the same - title = lift(i->string("t = ", run_info[1].time[i]), frame_index) + time = select_slice(run_info[1].time, :t; input=input, it=it) + title = lift(i->string("t = ", time[i]), frame_index) else - title = lift(i->join((string("t", irun, " = ", ri.time[i]) + time = select_slice(ri.time, :t; input=input, it=it) + title = lift(i->join((string("t", irun, " = ", time[i]) for (irun,ri) ∈ enumerate(run_info)), "; "), frame_index) end @@ -1849,14 +1894,14 @@ for dim ∈ one_dimension_combinations_no_t function $function_name(run_info, var_name; is=1, data=nothing, input=nothing, frame_index=nothing, ax=nothing, fig=nothing, outfile=nothing, yscale=nothing, - ylims=nothing, axis_args=Dict{Symbol,Any}(), - it=nothing, ir=nothing, iz=nothing, ivperp=nothing, - ivpa=nothing, ivzeta=nothing, ivr=nothing, - ivz=nothing, kwargs...) + ylims=nothing, label=nothing, + axis_args=Dict{Symbol,Any}(), it=nothing, ir=nothing, + iz=nothing, ivperp=nothing, ivpa=nothing, + ivzeta=nothing, ivr=nothing, ivz=nothing, kwargs...) if input === nothing if run_info.dfns if var_name ∈ keys(input_dict_dfns) - input = input_dict[var_name] + input = input_dict_dfns[var_name] else input = input_dict_dfns end @@ -1891,20 +1936,24 @@ for dim ∈ one_dimension_combinations_no_t ind = frame_index end if ax === nothing - title = lift(i->string("t = ", run_info.time[i]), ind) + time = select_slice(run_info.time, :t; input=input, it=it) + title = lift(i->string("t = ", time[i]), ind) fig, ax = get_1d_ax(; xlabel="$($dim_str)", ylabel=get_variable_symbol(var_name), yscale=yscale, title=title, axis_args...) else fig = nothing end + if label === nothing + label = run_info.run_name + end x = $dim_grid if $idim !== nothing x = x[$idim] end animate_1d(x, data; ax=ax, ylims=ylims, frame_index=ind, - label=run_info.run_name, kwargs...) + label=label, kwargs...) if input.show_element_boundaries && fig !== nothing element_boundary_inds = @@ -1941,7 +1990,7 @@ end for (dim1, dim2) ∈ two_dimension_combinations_no_t function_name_str = "animate_vs_$(dim2)_$(dim1)" function_name = Symbol(function_name_str) - spaces = " " ^ length(function_name_str) + spaces = " " ^ (length(function_name_str) + 1) dim1_str = String(dim1) dim2_str = String(dim2) dim1_grid = :( run_info.$dim1.grid ) @@ -1952,20 +2001,20 @@ for (dim1, dim2) ∈ two_dimension_combinations_no_t export $function_name """ - function $($function_name_str)(run_info::Tuple, var_name; is=1, data=nothing, - $($spaces)input=nothing, outfile=nothing, colorscale=identity, - $($spaces)transform=identity, axis_args=Dict{Symbol,Any}(), - $($spaces)it=nothing, ir=nothing, iz=nothing, ivperp=nothing, - $($spaces)ivpa=nothing, ivzeta=nothing, ivr=nothing, ivz=nothing, - $($spaces)kwargs...) - function $($function_name_str)(run_info, var_name; is=1, data=nothing, - $($spaces)input=nothing, frame_index=nothing, ax=nothing, - $($spaces)fig=nothing, colorbar_place=colorbar_place, - $($spaces)title=nothing, outfile=nothing, colorscale=identity, - $($spaces)transform=identity, axis_args=Dict{Symbol,Any}(), - $($spaces)it=nothing, ir=nothing, iz=nothing, ivperp=nothing, - $($spaces)ivpa=nothing, ivzeta=nothing, ivr=nothing, ivz=nothing, - $($spaces)kwargs...) + $($function_name_str)(run_info::Tuple, var_name; is=1, data=nothing, + $($spaces)input=nothing, outfile=nothing, colorscale=identity, + $($spaces)transform=identity, axis_args=Dict{Symbol,Any}(), + $($spaces)it=nothing, ir=nothing, iz=nothing, ivperp=nothing, + $($spaces)ivpa=nothing, ivzeta=nothing, ivr=nothing, ivz=nothing, + $($spaces)kwargs...) + $($function_name_str)(run_info, var_name; is=1, data=nothing, + $($spaces)input=nothing, frame_index=nothing, ax=nothing, + $($spaces)fig=nothing, colorbar_place=colorbar_place, + $($spaces)title=nothing, outfile=nothing, colorscale=identity, + $($spaces)transform=identity, axis_args=Dict{Symbol,Any}(), + $($spaces)it=nothing, ir=nothing, iz=nothing, ivperp=nothing, + $($spaces)ivpa=nothing, ivzeta=nothing, ivr=nothing, ivz=nothing, + $($spaces)kwargs...) Animate `var_name` from the run(s) represented by `run_info` (as returned by [`get_run_info`](@ref))vs $($dim1_str) and $($dim2_str). @@ -2036,10 +2085,12 @@ for (dim1, dim2) ∈ two_dimension_combinations_no_t if length(run_info) > 1 title = get_variable_symbol(var_name) - subtitles = (lift(i->string(ri.run_name, "\nt = ", ri.time[i]), + time = select_slice(ri.time, :t; input=input, it=it) + subtitles = (lift(i->string(ri.run_name, "\nt = ", time[i]), frame_index) for ri ∈ run_info) else + time = select_slice(run_info[1].time, :t; input=input, it=it) title = lift(i->string(get_variable_symbol(var_name), "\nt = ", run_info[1].time[i]), frame_index) @@ -2080,7 +2131,7 @@ for (dim1, dim2) ∈ two_dimension_combinations_no_t if input === nothing if run_info.dfns if var_name ∈ keys(input_dict_dfns) - input = input_dict[var_name] + input = input_dict_dfns[var_name] else input = input_dict_dfns end @@ -2122,6 +2173,7 @@ for (dim1, dim2) ∈ two_dimension_combinations_no_t colormap = input.colormap end if title === nothing && ax == nothing + time = select_slice(run_info.time, :t; input=input, it=it) title = lift(i->string(get_variable_symbol(var_name), "\nt = ", run_info.time[i]), ind) @@ -3524,24 +3576,28 @@ function calculate_steady_state_residual end function calculate_steady_state_residual(run_info::Tuple, variable_name; is=1, data=nothing, plot_prefix=nothing, fig_axes=nothing) - n_runs = length(run_info) - if data === nothing - data = Tuple(nothing for _ ∈ 1:n_runs) - end - if fig_axes === nothing - fig_axes = _get_steady_state_residual_fig_axes(length(run_info)) - end + try + n_runs = length(run_info) + if data === nothing + data = Tuple(nothing for _ ∈ 1:n_runs) + end + if fig_axes === nothing + fig_axes = _get_steady_state_residual_fig_axes(length(run_info)) + end - for (i, (ri, d)) ∈ enumerate(zip(run_info, data)) - calculate_steady_state_residual(ri, variable_name; is=is, data=d, - fig_axes=fig_axes, i_run=i) - end + for (i, (ri, d)) ∈ enumerate(zip(run_info, data)) + calculate_steady_state_residual(ri, variable_name; is=is, data=d, + fig_axes=fig_axes, i_run=i) + end - if plot_prefix !== nothing - _save_residual_plots(fig_axes, plot_prefix) - end + if plot_prefix !== nothing + _save_residual_plots(fig_axes, plot_prefix) + end - return fig_axes + return fig_axes + catch e + println("Error in calculate_steady_state_residual(). Error was ", e) + end end function calculate_steady_state_residual(run_info, variable_name; is=1, data=nothing, @@ -4302,9 +4358,9 @@ function animate_f_unnorm_vs_vpa_z(run_info; input=nothing, neutral=false, is=1, end """ - plot_charged_pdf_2D_at_wall(run_info; plot_prefix) + plot_ion_pdf_2D_at_wall(run_info; plot_prefix) -Make plots/animations of the charged particle distribution function at wall boundaries. +Make plots/animations of the ion distribution function at wall boundaries. The information for the runs to plot is passed in `run_info` (as returned by [`get_run_info`](@ref)). If `run_info` is a Tuple, comparison plots are made where line @@ -4318,7 +4374,7 @@ will be saved with the format `plot_prefix.pdf`. When ` is not a Tuple, `plot_prefix` is optional - plots/animations will be saved only if it is passed. """ -function plot_charged_pdf_2D_at_wall(run_info; plot_prefix) +function plot_ion_pdf_2D_at_wall(run_info; plot_prefix) input = Dict_to_NamedTuple(input_dict_dfns["wall_pdf"]) if !(input.plot || input.animate || input.advection_velocity) # nothing to do @@ -4332,7 +4388,7 @@ function plot_charged_pdf_2D_at_wall(run_info; plot_prefix) z_lower = 1 z_upper = run_info[1].z.n if !all(ri.z.n == z_upper for ri ∈ run_info) - println("Cannot run plot_charged_pdf_2D_at_wall() for runs with different " + println("Cannot run plot_ion_pdf_2D_at_wall() for runs with different " * "z-grid sizes. Got $(Tuple(ri.z.n for ri ∈ run_info))") return nothing end @@ -4582,6 +4638,338 @@ function plot_neutral_pdf_2D_at_wall(run_info; plot_prefix) return nothing end +""" + constraints_plots(run_info; plot_prefix=plot_prefix) + +Plot and/or animate the coefficients used to correct the normalised distribution +function(s) (aka shape functions) to obey the moment constraints. + +If there were no discretisation errors, we would have \$A=1\$, \$B=0\$, \$C=0\$. The +plots/animations show \$(A-1)\$ so that all three coefficients can be shown nicely on the +same axes. +""" +function constraints_plots(run_info; plot_prefix=plot_prefix) + input = Dict_to_NamedTuple(input_dict["constraints"]) + + if !(input.plot || input.animate) + return nothing + end + + try + println("Making plots of moment constraints coefficients") + + if !isa(run_info, Tuple) + run_info = (run_info,) + end + + it0 = input.it0 + ir0 = input.ir0 + + if input.plot + if any(ri.evolve_density || ri.evolve_upar || ri.evolve_ppar + for ri ∈ run_info) + + # Ions + frame_index = Observable(1) + fig, ax = get_1d_ax(; xlabel="z", ylabel="constraint coefficient") + for ri ∈ run_info + if !(ri.evolve_density || ri.evolve_upar || ri.evolve_ppar) + continue + end + nspecies = ri.n_ion_species + for is ∈ 1:nspecies + if length(run_info) > 1 + prefix = ri.run_name * ", " + else + prefix = "" + end + if nspecies > 1 + suffix = ", species $is" + else + suffix = "" + end + + varname = "ion_constraints_A_coefficient" + label = prefix * "(A-1)" * suffix + data = get_variable(ri, varname; it=it0, is=is, ir=ir0) + data .-= 1.0 + plot_vs_z(ri, varname; label=label, data=data, ax=ax, input=input) + + varname = "ion_constraints_B_coefficient" + label = prefix * "B" * suffix + plot_vs_z(ri, varname; label=label, ax=ax, it=it0, is=is, ir=ir0, + input=input) + + varname = "ion_constraints_C_coefficient" + label = prefix * "C" * suffix + plot_vs_z(ri, varname; label=label, ax=ax, it=it0, is=is, ir=ir0, + input=input) + end + end + put_legend_right(fig, ax) + save(plot_prefix * "ion_constraints.pdf", fig) + end + + # Neutrals + if any(ri.n_neutral_species > 1 + && (ri.evolve_density || ri.evolve_upar || ri.evolve_ppar) + for ri ∈ run_info) + + fig, ax = get_1d_ax(; xlabel="z", ylabel="constraint coefficient") + for ri ∈ run_info + if !(ri.evolve_density || ri.evolve_upar || ri.evolve_ppar) + continue + end + nspecies = ri.n_neutral_species + for is ∈ 1:nspecies + if length(run_info) > 1 + prefix = ri.run_name * ", " + else + prefix = "" + end + if nspecies > 1 + suffix = ", species $is" + else + suffix = "" + end + + varname = "neutral_constraints_A_coefficient" + label = prefix * "(A-1)" * suffix + data = get_variable(ri, varname; it=it0, is=is, ir=ir0) + data .-= 1.0 + plot_vs_z(ri, varname; label=label, data=data, ax=ax, input=input) + + varname = "neutral_constraints_B_coefficient" + label = prefix * "B" * suffix + plot_vs_z(ri, varname; label=label, ax=ax, it=it0, is=is, ir=ir0, + input=input) + + varname = "neutral_constraints_C_coefficient" + label = prefix * "C" * suffix + plot_vs_z(ri, varname; label=label, ax=ax, it=it0, is=is, ir=ir0, + input=input) + end + end + put_legend_right(fig, ax) + save(plot_prefix * "neutral_constraints.pdf", fig) + end + + # Electrons + if any(ri.composition.electron_physics == kinetic_electrons for ri ∈ run_info) + + fig, ax = get_1d_ax(; xlabel="z", ylabel="constraint coefficient") + for ri ∈ run_info + if length(run_info) > 1 + prefix = ri.run_name * ", " + else + prefix = "" + end + + varname = "electron_constraints_A_coefficient" + label = prefix * "(A-1)" + data = get_variable(ri, varname; it=it0, ir=ir0) + data .-= 1.0 + plot_vs_z(ri, varname; label=label, data=data, ax=ax, input=input) + + varname = "electron_constraints_B_coefficient" + label = prefix * "B" + plot_vs_z(ri, varname; label=label, ax=ax, it=it0, ir=ir0, + input=input) + + varname = "electron_constraints_C_coefficient" + label = prefix * "C" + plot_vs_z(ri, varname; label=label, ax=ax, it=it0, ir=ir0, + input=input) + end + put_legend_right(fig, ax) + save(plot_prefix * "electron_constraints.pdf", fig) + end + end + + if input.animate + nt = minimum(ri.nt for ri ∈ run_info) + + if any(ri.evolve_density || ri.evolve_upar || ri.evolve_ppar + for ri ∈ run_info) + + # Ions + frame_index = Observable(1) + fig, ax = get_1d_ax(; xlabel="z", ylabel="constraint coefficient") + + # Calculate plot limits manually so we can exclude the first time point, which + # often has a large value for (A-1) due to the way initialisation is done, + # which can make the subsequent values hard to see. + ymin = Inf + ymax = -Inf + for ri ∈ run_info + if !(ri.evolve_density || ri.evolve_upar || ri.evolve_ppar) + continue + end + nspecies = ri.n_ion_species + for is ∈ 1:nspecies + if length(run_info) > 1 + prefix = ri.run_name * ", " + else + prefix = "" + end + if nspecies > 1 + suffix = ", species $is" + else + suffix = "" + end + + varname = "ion_constraints_A_coefficient" + label = prefix * "(A-1)" * suffix + data = get_variable(ri, varname; is=is, ir=ir0) + data .-= 1.0 + ymin = min(ymin, minimum(data[:,2:end])) + ymax = max(ymax, maximum(data[:,2:end])) + animate_vs_z(ri, varname; label=label, data=data, + frame_index=frame_index, ax=ax, input=input) + + varname = "ion_constraints_B_coefficient" + label = prefix * "B" * suffix + data = get_variable(ri, varname; is=is, ir=ir0) + ymin = min(ymin, minimum(data[:,2:end])) + ymax = max(ymax, maximum(data[:,2:end])) + animate_vs_z(ri, varname; label=label, data=data, + frame_index=frame_index, ax=ax, is=is, ir=ir0, + input=input) + + varname = "ion_constraints_C_coefficient" + label = prefix * "C" * suffix + data = get_variable(ri, varname; is=is, ir=ir0) + ymin = min(ymin, minimum(data[:,2:end])) + ymax = max(ymax, maximum(data[:,2:end])) + animate_vs_z(ri, varname; label=label, data=data, + frame_index=frame_index, ax=ax, is=is, ir=ir0, + input=input) + end + end + put_legend_right(fig, ax) + ylims!(ax, ymin, ymax) + save_animation(fig, frame_index, nt, + plot_prefix * "ion_constraints." * input.animation_ext) + end + + # Neutrals + if any(ri.n_neutral_species > 1 + && (ri.evolve_density || ri.evolve_upar || ri.evolve_ppar) + for ri ∈ run_info) + + frame_index = Observable(1) + fig, ax = get_1d_ax(; xlabel="z", ylabel="constraint coefficient") + + # Calculate plot limits manually so we can exclude the first time point, which + # often has a large value for (A-1) due to the way initialisation is done, + # which can make the subsequent values hard to see. + ymin = Inf + ymax = -Inf + for ri ∈ run_info + if !(ri.evolve_density || ri.evolve_upar || ri.evolve_ppar) + continue + end + nspecies = ri.n_neutral_species + for is ∈ 1:nspecies + if length(run_info) > 1 + prefix = ri.run_name * ", " + else + prefix = "" + end + if nspecies > 1 + suffix = ", species $is" + else + suffix = "" + end + + varname = "neutral_constraints_A_coefficient" + label = prefix * "(A-1)" * suffix + data = get_variable(ri, varname; is=is, ir=ir0) + data .-= 1.0 + ymin = min(ymin, minimum(data[:,2:end])) + ymax = max(ymax, maximum(data[:,2:end])) + animate_vs_z(ri, varname; label=label, data=data, + frame_index=frame_index, ax=ax, input=input) + + varname = "neutral_constraints_B_coefficient" + label = prefix * "B" * suffix + data = get_variable(ri, varname; is=is, ir=ir0) + ymin = min(ymin, minimum(data[:,2:end])) + ymax = max(ymax, maximum(data[:,2:end])) + animate_vs_z(ri, varname; label=label, data=data, + frame_index=frame_index, ax=ax, is=is, ir=ir0, + input=input) + + varname = "neutral_constraints_C_coefficient" + label = prefix * "C" * suffix + data = get_variable(ri, varname; is=is, ir=ir0) + ymin = min(ymin, minimum(data[:,2:end])) + ymax = max(ymax, maximum(data[:,2:end])) + animate_vs_z(ri, varname; label=label, data=data, + frame_index=frame_index, ax=ax, is=is, ir=ir0, + input=input) + end + end + put_legend_right(fig, ax) + ylims!(ax, ymin, ymax) + save_animation(fig, frame_index, nt, + plot_prefix * "neutral_constraints." * input.animation_ext) + end + + # Electrons + if any(ri.composition.electron_physics == kinetic_electrons for ri ∈ run_info) + + frame_index = Observable(1) + fig, ax = get_1d_ax(; xlabel="z", ylabel="constraint coefficient") + + # Calculate plot limits manually so we can exclude the first time point, which + # often has a large value for (A-1) due to the way initialisation is done, + # which can make the subsequent values hard to see. + ymin = Inf + ymax = -Inf + for ri ∈ run_info + if length(run_info) > 1 + prefix = ri.run_name * ", " + else + prefix = "" + end + + varname = "electron_constraints_A_coefficient" + label = prefix * "(A-1)" + data = get_variable(ri, varname; ir=ir0) + data .-= 1.0 + ymin = min(ymin, minimum(data[:,2:end])) + ymax = max(ymax, maximum(data[:,2:end])) + animate_vs_z(ri, varname; label=label, data=data, + frame_index=frame_index, ax=ax, input=input) + + varname = "electron_constraints_B_coefficient" + label = prefix * "B" + data = get_variable(ri, varname; ir=ir0) + ymin = min(ymin, minimum(data[:,2:end])) + ymax = max(ymax, maximum(data[:,2:end])) + animate_vs_z(ri, varname; label=label, data=data, + frame_index=frame_index, ax=ax, ir=ir0, input=input) + + varname = "electron_constraints_C_coefficient" + label = prefix * "C" + data = get_variable(ri, varname; ir=ir0) + ymin = min(ymin, minimum(data[:,2:end])) + ymax = max(ymax, maximum(data[:,2:end])) + animate_vs_z(ri, varname; label=label, data=data, + frame_index=frame_index, ax=ax, ir=ir0, input=input) + end + put_legend_right(fig, ax) + ylims!(ax, ymin, ymax) + save_animation(fig, frame_index, nt, + plot_prefix * "electron_constraints." * input.animation_ext) + end + end + catch e + println("Error in constraints_plots(). Error was ", e) + end +end + """ Chodura_condition_plots(run_info::Tuple; plot_prefix) Chodura_condition_plots(run_info; plot_prefix=nothing, axes=nothing) @@ -5772,7 +6160,7 @@ end field_sym_label, norm_label, plot_dims, animate_dims) Utility function for making plots to avoid duplicated code in -[`compare_charged_pdf_symbolic_test`](@ref) and +[`compare_ion_pdf_symbolic_test`](@ref) and [`compare_neutral_pdf_symbolic_test`](@ref). The information for the run to analyse is passed in `run_info` (as returned by @@ -5922,7 +6310,7 @@ function _MMS_pdf_plots(run_info, input, variable_name, plot_prefix, field_label end """ - compare_charged_pdf_symbolic_test(run_info, plot_prefix; io=nothing, + compare_ion_pdf_symbolic_test(run_info, plot_prefix; io=nothing, input=nothing) Compare the computed and manufactured solutions for the ion distribution function. @@ -5943,7 +6331,7 @@ Note: when calculating error norms, data is loaded only for 1 time point and for chunk that is the same size as computed by 1 block of the simulation at run time. This should prevent excessive memory requirements for this function. """ -function compare_charged_pdf_symbolic_test(run_info, plot_prefix; io=nothing, +function compare_ion_pdf_symbolic_test(run_info, plot_prefix; io=nothing, input=nothing) field_label = L"\tilde{f}_i" @@ -6473,7 +6861,7 @@ function manufactured_solutions_analysis_dfns(run_info; plot_prefix) println_to_stdout_and_file(io, "# ", run_info.run_name) println_to_stdout_and_file(io, join(run_info.time, " "), " # time / (Lref/cref): ") - compare_charged_pdf_symbolic_test(run_info, plot_prefix; io=io, input=input) + compare_ion_pdf_symbolic_test(run_info, plot_prefix; io=io, input=input) if run_info.n_neutral_species > 0 compare_neutral_pdf_symbolic_test(run_info, plot_prefix; io=io, input=input) @@ -6739,7 +7127,7 @@ function timestep_diagnostics(run_info; plot_prefix=nothing, it=nothing) end data = get_variable(run_info, "CFL_ion_z") datamin = minimum(minimum(d) for d ∈ data) - animate_vs_vpa_z(run_info, "CFL_ion_z"; data=data, + animate_vs_vpa_z(run_info, "CFL_ion_z"; data=data, it=it, outfile=plot_prefix * "CFL_ion_z_vs_vpa_z.gif", colorscale=log10, transform=x->positive_or_nan(x; epsilon=1.e-30), @@ -6750,7 +7138,7 @@ function timestep_diagnostics(run_info; plot_prefix=nothing, it=nothing) :rightspinevisible=>false)) data = get_variable(run_info, "CFL_ion_vpa") datamin = minimum(minimum(d) for d ∈ data) - animate_vs_vpa_z(run_info, "CFL_ion_vpa"; data=data, + animate_vs_vpa_z(run_info, "CFL_ion_vpa"; data=data, it=it, outfile=plot_prefix * "CFL_ion_vpa_vs_vpa_z.gif", colorscale=log10, transform=x->positive_or_nan(x; epsilon=1.e-30), @@ -6762,7 +7150,7 @@ function timestep_diagnostics(run_info; plot_prefix=nothing, it=nothing) if any(ri.n_neutral_species > 0 for ri ∈ run_info) data = get_variable(run_info, "CFL_neutral_z") datamin = minimum(minimum(d) for d ∈ data) - animate_vs_vz_z(run_info, "CFL_neutral_z"; data=data, + animate_vs_vz_z(run_info, "CFL_neutral_z"; data=data, it=it, outfile=plot_prefix * "CFL_neutral_z_vs_vz_z.gif", colorscale=log10, transform=x->positive_or_nan(x; epsilon=1.e-30), @@ -6773,7 +7161,7 @@ function timestep_diagnostics(run_info; plot_prefix=nothing, it=nothing) :rightspinevisible=>false)) data = get_variable(run_info, "CFL_neutral_vz") datamin = minimum(minimum(d) for d ∈ data) - animate_vs_vz_z(run_info, "CFL_neutral_vz"; data=data, + animate_vs_vz_z(run_info, "CFL_neutral_vz"; data=data, it=it, outfile=plot_prefix * "CFL_neutral_vz_vs_vz_z.gif", colorscale=log10, transform=x->positive_or_nan(x; epsilon=1.e-30), diff --git a/moment_kinetics/debug_test/README.md b/moment_kinetics/debug_test/README.md index 76c190791..10eafdf65 100644 --- a/moment_kinetics/debug_test/README.md +++ b/moment_kinetics/debug_test/README.md @@ -96,7 +96,7 @@ Suggested debugging strategy for race conditions is: failure. Usually a failure should indicate where there is a missing `begin_*_region()` call. There may be places though where synchronization is required even though the type of loop macros used does not change (for - example when `phi` is calculated contributions from all charged species need + example when `phi` is calculated contributions from all ion species need to be summed, resulting in an unusual pattern of array accesses); in this case `_block_synchronize()` can be called directly. * The function `debug_check_shared_memory()` can be inserted between diff --git a/moment_kinetics/debug_test/restart_interpolation_inputs.jl b/moment_kinetics/debug_test/restart_interpolation_inputs.jl index dbfda87d7..c914bc231 100644 --- a/moment_kinetics/debug_test/restart_interpolation_inputs.jl +++ b/moment_kinetics/debug_test/restart_interpolation_inputs.jl @@ -59,7 +59,8 @@ base_input = Dict( "vzeta_nelement" => 1, "vr_ngrid" => 1, "vr_nelement" => 1, - "numerical_dissipation" => Dict{String,Any}("force_minimum_pdf_value" => 0.0)) + "ion_numerical_dissipation" => Dict{String,Any}("force_minimum_pdf_value" => 0.0), + "neutral_numerical_dissipation" => Dict{String,Any}("force_minimum_pdf_value" => 0.0)) test_input = merge(base_input, diff --git a/moment_kinetics/debug_test/runtest_template.jl b/moment_kinetics/debug_test/runtest_template.jl index 779be8929..6f1cb0b4e 100644 --- a/moment_kinetics/debug_test/runtest_template.jl +++ b/moment_kinetics/debug_test/runtest_template.jl @@ -1,7 +1,8 @@ using moment_kinetics: setup_moment_kinetics, cleanup_moment_kinetics! using moment_kinetics.time_advance: time_advance! using moment_kinetics.communication -using moment_kinetics.looping: dimension_combinations, anyv_dimension_combinations +using moment_kinetics.looping: all_dimensions, dimension_combinations, + anyv_dimension_combinations using moment_kinetics.Glob using moment_kinetics.Primes @@ -71,8 +72,39 @@ function runtests(; restart=false) else dims_to_test = debug_loop_type end + for d ∈ all_dimensions + nelement_name = "$(d)_nelement" + if nelement_name ∈ keys(input) + nelement = input[nelement_name] + elseif d ∈ (:vperp, :vzeta, :vr) + nelement = 1 + else + # Dummy value, here it only matters if this is 1 or greater than 1 + nelement = 2 + end + + ngrid_name = "$(d)_ngrid" + if ngrid_name ∈ keys(input) + ngrid = input[ngrid_name] + elseif d ∈ (:vperp, :vzeta, :vr) + ngrid = 1 + else + # Dummy value, here it only matters if this is 1 or greater than 1 + ngrid = 2 + end + + if nelement == 1 && ngrid == 1 + # Dimension has only one point, so cannot be parallelised - no need to + # test + dims_to_test = Tuple(x for x ∈ dims_to_test if x != d) + end + end ndims = length(dims_to_test) + if ndims == 0 + # No dimensions to test here + continue + end for i ∈ 1:(ndims+n_factors-1)÷n_factors debug_loop_parallel_dims = dims_to_test[(i-1)*n_factors+1:min(i*n_factors, ndims)] diff --git a/moment_kinetics/debug_test/sound_wave_inputs.jl b/moment_kinetics/debug_test/sound_wave_inputs.jl index 4439c8a99..5814c5662 100644 --- a/moment_kinetics/debug_test/sound_wave_inputs.jl +++ b/moment_kinetics/debug_test/sound_wave_inputs.jl @@ -257,22 +257,22 @@ test_input_chebyshev_cx0_1D1V_split_3_moments = "evolve_moments_parallel_pressure" => true)) test_input_list = [ - #test_input_finite_difference, + test_input_finite_difference, #test_input_finite_difference_split_1_moment, #test_input_finite_difference_split_2_moments, #test_input_finite_difference_split_3_moments, - #test_input_finite_difference_cx0, + test_input_finite_difference_cx0, #test_input_finite_difference_cx0_split_1_moment, #test_input_finite_difference_cx0_split_2_moments, #test_input_finite_difference_cx0_split_3_moments, - #test_input_finite_difference_1D1V, - #test_input_finite_difference_1D1V_split_1_moment, - #test_input_finite_difference_1D1V_split_2_moments, - #test_input_finite_difference_1D1V_split_3_moments, - #test_input_finite_difference_cx0_1D1V, - #test_input_finite_difference_cx0_1D1V_split_1_moment, - #test_input_finite_difference_cx0_1D1V_split_2_moments, - #test_input_finite_difference_cx0_1D1V_split_3_moments, + test_input_finite_difference_1D1V, + test_input_finite_difference_1D1V_split_1_moment, + test_input_finite_difference_1D1V_split_2_moments, + test_input_finite_difference_1D1V_split_3_moments, + test_input_finite_difference_cx0_1D1V, + test_input_finite_difference_cx0_1D1V_split_1_moment, + test_input_finite_difference_cx0_1D1V_split_2_moments, + test_input_finite_difference_cx0_1D1V_split_3_moments, test_input_chebyshev, #test_input_chebyshev_split_1_moment, #test_input_chebyshev_split_2_moments, diff --git a/moment_kinetics/ext/manufactured_solns_ext.jl b/moment_kinetics/ext/manufactured_solns_ext.jl index 2bad8dcf0..cda63a819 100644 --- a/moment_kinetics/ext/manufactured_solns_ext.jl +++ b/moment_kinetics/ext/manufactured_solns_ext.jl @@ -450,7 +450,7 @@ using IfElse geometry_input_data::geometry_input, composition, species, nr, nvperp) # calculate the geometry symbolically geometry = geometry_sym(geometry_input_data,Lz,Lr,nr) - charged_species = species.charged[1] + ion_species = species.ion[1] if composition.n_neutral_species > 0 neutral_species = species.neutral[1] else @@ -458,17 +458,17 @@ using IfElse end densi = densi_sym(Lr, Lz, r_bc, z_bc, composition, manufactured_solns_input, - charged_species) + ion_species) upari = upari_sym(Lr, Lz, r_bc, z_bc, composition, geometry, nr, manufactured_solns_input, - charged_species) + ion_species) ppari = ppari_sym(Lr, Lz, r_bc, z_bc, composition, manufactured_solns_input, - charged_species) + ion_species) pperpi = pperpi_sym(Lr, Lz, r_bc, z_bc, composition, manufactured_solns_input, - charged_species, nvperp) + ion_species, nvperp) vthi = vthi_sym(Lr, Lz, r_bc, z_bc, composition, manufactured_solns_input, - charged_species, nvperp) + ion_species, nvperp) dfni = dfni_sym(Lr, Lz, r_bc, z_bc, composition, geometry, nr, - manufactured_solns_input, charged_species) + manufactured_solns_input, ion_species) densn = densn_sym(Lr, Lz, r_bc, z_bc, geometry,composition, manufactured_solns_input, neutral_species) @@ -538,7 +538,7 @@ using IfElse geometry_input_data::geometry_input, collisions, num_diss_params, species) geometry = geometry_sym(geometry_input_data,z_coord.L,r_coord.L,r_coord.n) - charged_species = species.charged[1] + ion_species = species.ion[1] if composition.n_neutral_species > 0 neutral_species = species.neutral[1] else @@ -547,16 +547,16 @@ using IfElse # ion manufactured solutions densi = densi_sym(r_coord.L, z_coord.L, r_coord.bc, z_coord.bc, composition, - manufactured_solns_input, charged_species) - upari = upari_sym(r_coord.L, z_coord.L, r_coord.bc, z_coord.bc, composition, geometry, r_coord.n, manufactured_solns_input, charged_species) + manufactured_solns_input, ion_species) + upari = upari_sym(r_coord.L, z_coord.L, r_coord.bc, z_coord.bc, composition, geometry, r_coord.n, manufactured_solns_input, ion_species) vthi = vthi_sym(r_coord.L, z_coord.L, r_coord.bc, z_coord.bc, composition, manufactured_solns_input, - charged_species, vperp_coord.n) + ion_species, vperp_coord.n) dfni = dfni_sym(r_coord.L, z_coord.L, r_coord.bc, z_coord.bc, composition, - geometry, r_coord.n, manufactured_solns_input, charged_species) + geometry, r_coord.n, manufactured_solns_input, ion_species) #dfni in vr vz vzeta coordinates vrvzvzeta_dfni = cartesian_dfni_sym(r_coord.L, z_coord.L, r_coord.bc, z_coord.bc, composition, manufactured_solns_input, - charged_species) + ion_species) # neutral manufactured solutions densn = densn_sym(r_coord.L,z_coord.L, r_coord.bc, z_coord.bc, geometry, @@ -604,7 +604,7 @@ using IfElse # calculate the electric fields and the potential Er, Ez, phi = electric_fields(r_coord.L, z_coord.L, r_coord.bc, z_coord.bc, composition, r_coord.n, manufactured_solns_input, - charged_species) + ion_species) # the adiabatic invariant (for compactness) mu = 0.5*(vperp^2)/Bmag @@ -638,31 +638,31 @@ using IfElse Si += - nuii_krook*(FMaxwellian - dfni) end include_num_diss_in_MMS = true - if num_diss_params.vpa_dissipation_coefficient > 0.0 && include_num_diss_in_MMS - Si += - num_diss_params.vpa_dissipation_coefficient*Dvpa(Dvpa(dfni)) + if num_diss_params.ion.vpa_dissipation_coefficient > 0.0 && include_num_diss_in_MMS + Si += - num_diss_params.ion.vpa_dissipation_coefficient*Dvpa(Dvpa(dfni)) end - if num_diss_params.vperp_dissipation_coefficient > 0.0 && include_num_diss_in_MMS - Si += - num_diss_params.vperp_dissipation_coefficient*Dvperp(Dvperp(dfni)) + if num_diss_params.ion.vperp_dissipation_coefficient > 0.0 && include_num_diss_in_MMS + Si += - num_diss_params.ion.vperp_dissipation_coefficient*Dvperp(Dvperp(dfni)) end - if num_diss_params.r_dissipation_coefficient > 0.0 && include_num_diss_in_MMS - Si += - rfac*num_diss_params.r_dissipation_coefficient*Dr(Dr(dfni)) + if num_diss_params.ion.r_dissipation_coefficient > 0.0 && include_num_diss_in_MMS + Si += - rfac*num_diss_params.ion.r_dissipation_coefficient*Dr(Dr(dfni)) end - if num_diss_params.z_dissipation_coefficient > 0.0 && include_num_diss_in_MMS - Si += - num_diss_params.z_dissipation_coefficient*Dz(Dz(dfni)) + if num_diss_params.ion.z_dissipation_coefficient > 0.0 && include_num_diss_in_MMS + Si += - num_diss_params.ion.z_dissipation_coefficient*Dz(Dz(dfni)) end Source_i = expand_derivatives(Si) # the neutral source to maintain the manufactured solution Sn = Dt(dfnn) + vz * Dz(dfnn) + rfac*vr * Dr(dfnn) + cx_frequency* (densi*dfnn - densn*vrvzvzeta_dfni) + ionization_frequency*dense*dfnn - if num_diss_params.vz_dissipation_coefficient > 0.0 && include_num_diss_in_MMS - Sn += - num_diss_params.vz_dissipation_coefficient*Dvz(Dvz(dfnn)) + if num_diss_params.neutral.vz_dissipation_coefficient > 0.0 && include_num_diss_in_MMS + Sn += - num_diss_params.neutral.vz_dissipation_coefficient*Dvz(Dvz(dfnn)) end - if num_diss_params.r_dissipation_coefficient > 0.0 && include_num_diss_in_MMS - Sn += - rfac*num_diss_params.r_dissipation_coefficient*Dr(Dr(dfnn)) + if num_diss_params.neutral.r_dissipation_coefficient > 0.0 && include_num_diss_in_MMS + Sn += - rfac*num_diss_params.neutral.r_dissipation_coefficient*Dr(Dr(dfnn)) end - if num_diss_params.z_dissipation_coefficient > 0.0 && include_num_diss_in_MMS - Sn += - num_diss_params.z_dissipation_coefficient*Dz(Dz(dfnn)) + if num_diss_params.neutral.z_dissipation_coefficient > 0.0 && include_num_diss_in_MMS + Sn += - num_diss_params.neutral.z_dissipation_coefficient*Dz(Dz(dfnn)) end Source_n = expand_derivatives(Sn) diff --git a/moment_kinetics/src/analysis.jl b/moment_kinetics/src/analysis.jl index 7daddfd17..fc342e050 100644 --- a/moment_kinetics/src/analysis.jl +++ b/moment_kinetics/src/analysis.jl @@ -10,10 +10,10 @@ using ..array_allocation: allocate_float using ..calculus: integral using ..communication using ..coordinates: coordinate -using ..initial_conditions: vpagrid_to_dzdt +using ..boundary_conditions: vpagrid_to_dzdt using ..interpolation: interpolate_to_grid_1d using ..load_data: open_readonly_output_file, get_nranks, load_pdf_data, load_rank_data -using ..load_data: load_distributed_charged_pdf_slice +using ..load_data: load_distributed_ion_pdf_slice using ..looping using ..type_definitions: mk_int using ..velocity_moments: integrate_over_vspace @@ -140,12 +140,12 @@ function check_Chodura_condition(r, z, vperp, vpa, dens, upar, vth, composition, end end if f_lower === nothing - f_lower = load_distributed_charged_pdf_slice(run_name, nblocks, t_range, + f_lower = load_distributed_ion_pdf_slice(run_name, nblocks, t_range, composition.n_ion_species, r, z, vperp, vpa; iz=1, ir=ir0) end if f_upper === nothing - f_upper = load_distributed_charged_pdf_slice(run_name, nblocks, t_range, + f_upper = load_distributed_ion_pdf_slice(run_name, nblocks, t_range, composition.n_ion_species, r, z, vperp, vpa; iz=z.n_global, ir=ir0) end @@ -647,17 +647,22 @@ function steady_state_square_residuals(variable, variable_at_previous_time, dt; if only_max_abs absolute_residual = - _steady_state_absolute_residual(variable, variable_at_previous_time, + _steady_state_absolute_residual(this_slice, this_slice_previous_time, reshaped_dt) # Need to wrap the maximum(...) in a call to vec(...) so that we return a # Vector, not an N-dimensional array where the first (N-1) dimensions all # have size 1. - local_max_absolute = max.(local_max_absolute, - vec(maximum(absolute_residual, - dims=tuple((1:t_dim-1)...)))) + this_dims = tuple((1:t_dim-3)...) + if this_dims === () + local_max_absolute = max.(local_max_absolute, [absolute_residual]) + else + local_max_absolute = max.(local_max_absolute, + vec(maximum(absolute_residual, + dims=this_dims))) + end else absolute_square_residual, relative_square_residual = - _steady_state_square_residual(variable, variable_at_previous_time, + _steady_state_square_residual(this_slice, this_slice_previous_time, reshaped_dt, epsilon, variable_max) # Need to wrap the sum(...) or maximum(...) in a call to vec(...) so that # we return a Vector, not an N-dimensional array where the first (N-1) diff --git a/moment_kinetics/src/boundary_conditions.jl b/moment_kinetics/src/boundary_conditions.jl new file mode 100644 index 000000000..61dff1a08 --- /dev/null +++ b/moment_kinetics/src/boundary_conditions.jl @@ -0,0 +1,1006 @@ +""" +Functions for applying boundary conditions +""" +module boundary_conditions + +export enforce_boundary_conditions! +export enforce_neutral_boundary_conditions! + +using SpecialFunctions: erfc + +using ..calculus: reconcile_element_boundaries_MPI! +using ..coordinates: coordinate +using ..interpolation: interpolate_to_grid_1d! +using ..looping +using ..moment_kinetics_structs: scratch_pdf +using ..type_definitions: mk_float, mk_int +using ..velocity_moments: integrate_over_vspace, integrate_over_neutral_vspace, + integrate_over_positive_vz, integrate_over_negative_vz + +""" +enforce boundary conditions in vpa and z on the evolved pdf; +also enforce boundary conditions in z on all separately evolved velocity space moments of the pdf +""" +function enforce_boundary_conditions!(f, f_r_bc, density, upar, ppar, moments, vpa_bc, + z_bc, r_bc, vpa, vperp, z, r, vpa_spectral, vperp_spectral, vpa_adv, vperp_adv, z_adv, r_adv, composition, scratch_dummy, + r_diffusion, vpa_diffusion, vperp_diffusion) + if vpa.n > 1 + begin_s_r_z_vperp_region() + @loop_s_r_z_vperp is ir iz ivperp begin + # enforce the vpa BC + # use that adv.speed independent of vpa + @views enforce_v_boundary_condition_local!(f[:,ivperp,iz,ir,is], vpa_bc, + vpa_adv[is].speed[:,ivperp,iz,ir], vpa_diffusion, + vpa, vpa_spectral) + end + end + if vperp.n > 1 + begin_s_r_z_vpa_region() + enforce_vperp_boundary_condition!(f, vperp.bc, vperp, vperp_spectral, + vperp_adv, vperp_diffusion) + end + if z.n > 1 + begin_s_r_vperp_vpa_region() + # enforce the z BC on the evolved velocity space moments of the pdf + enforce_z_boundary_condition_moments!(density, moments, z_bc) + enforce_z_boundary_condition!(f, density, upar, ppar, moments, z_bc, z_adv, z, + vperp, vpa, composition, + scratch_dummy.buffer_vpavperprs_1, + scratch_dummy.buffer_vpavperprs_2, + scratch_dummy.buffer_vpavperprs_3, + scratch_dummy.buffer_vpavperprs_4) + + end + if r.n > 1 + begin_s_z_vperp_vpa_region() + enforce_r_boundary_condition!(f, f_r_bc, r_bc, r_adv, vpa, vperp, z, r, + composition, scratch_dummy.buffer_vpavperpzs_1, + scratch_dummy.buffer_vpavperpzs_2, + scratch_dummy.buffer_vpavperpzs_3, + scratch_dummy.buffer_vpavperpzs_4, r_diffusion) + end +end +function enforce_boundary_conditions!(fvec_out::scratch_pdf, moments, f_r_bc, vpa_bc, + z_bc, r_bc, vpa, vperp, z, r, vpa_spectral, vperp_spectral, vpa_adv, vperp_adv, z_adv, r_adv, composition, scratch_dummy, + r_diffusion, vpa_diffusion, vperp_diffusion) + enforce_boundary_conditions!(fvec_out.pdf, f_r_bc, fvec_out.density, fvec_out.upar, + fvec_out.ppar, moments, vpa_bc, z_bc, r_bc, vpa, vperp, z, r, + vpa_spectral, vperp_spectral, vpa_adv, vperp_adv, z_adv, + r_adv, composition, scratch_dummy, r_diffusion, vpa_diffusion, vperp_diffusion) +end + +""" +enforce boundary conditions on f in r +""" +function enforce_r_boundary_condition!(f::AbstractArray{mk_float,5}, f_r_bc, bc::String, + adv, vpa, vperp, z, r, composition, end1::AbstractArray{mk_float,4}, + end2::AbstractArray{mk_float,4}, buffer1::AbstractArray{mk_float,4}, + buffer2::AbstractArray{mk_float,4}, r_diffusion::Bool) + + nr = r.n + + if r.nelement_global > r.nelement_local + # reconcile internal element boundaries across processes + # & enforce periodicity and external boundaries if needed + @loop_s_z_vperp_vpa is iz ivperp ivpa begin + end1[ivpa,ivperp,iz,is] = f[ivpa,ivperp,iz,1,is] + end2[ivpa,ivperp,iz,is] = f[ivpa,ivperp,iz,nr,is] + end + reconcile_element_boundaries_MPI!(f, end1, end2, buffer1, buffer2, r) + end + + # 'periodic' BC enforces periodicity by taking the average of the boundary points + # enforce the condition if r is local + if bc == "periodic" && r.nelement_global == r.nelement_local + @loop_s_z_vperp_vpa is iz ivperp ivpa begin + f[ivpa,ivperp,iz,1,is] = 0.5*(f[ivpa,ivperp,iz,nr,is]+f[ivpa,ivperp,iz,1,is]) + f[ivpa,ivperp,iz,nr,is] = f[ivpa,ivperp,iz,1,is] + end + end + if bc == "Dirichlet" + zero = 1.0e-10 + # use the old distribution to force the new distribution to have + # consistant-in-time values at the boundary + # with bc = "Dirichlet" and r_diffusion = false + # impose bc for incoming parts of velocity space only (Hyperbolic PDE) + # with bc = "Dirichlet" and r_diffusion = true + # impose bc on both sides of the domain to accomodate a diffusion operator d^2 / d r^2 + @loop_s_z_vperp_vpa is iz ivperp ivpa begin + ir = 1 # r = -L/2 -- check that the point is on lowest rank + if r.irank == 0 && (r_diffusion || adv[is].speed[ir,ivpa,ivperp,iz] > zero) + f[ivpa,ivperp,iz,ir,is] = f_r_bc[ivpa,ivperp,iz,1,is] + end + ir = r.n # r = L/2 -- check that the point is on highest rank + if r.irank == r.nrank - 1 && (r_diffusion || adv[is].speed[ir,ivpa,ivperp,iz] < -zero) + f[ivpa,ivperp,iz,ir,is] = f_r_bc[ivpa,ivperp,iz,end,is] + end + end + end +end + +""" +enforce boundary conditions on ion particle f in z +""" +function enforce_z_boundary_condition!(pdf, density, upar, ppar, moments, bc::String, adv, + z, vperp, vpa, composition, end1::AbstractArray{mk_float,4}, + end2::AbstractArray{mk_float,4}, buffer1::AbstractArray{mk_float,4}, + buffer2::AbstractArray{mk_float,4}) + # this block ensures periodic BC can be supported with distributed memory MPI + if z.nelement_global > z.nelement_local + # reconcile internal element boundaries across processes + # & enforce periodicity and external boundaries if needed + nz = z.n + @loop_s_r_vperp_vpa is ir ivperp ivpa begin + end1[ivpa,ivperp,ir,is] = pdf[ivpa,ivperp,1,ir,is] + end2[ivpa,ivperp,ir,is] = pdf[ivpa,ivperp,nz,ir,is] + end + # check on periodic bc happens inside this call below + reconcile_element_boundaries_MPI!(pdf, end1, end2, buffer1, buffer2, z) + end + # define a zero that accounts for finite precision + zero = 1.0e-14 + # 'constant' BC is time-independent f at upwind boundary + # and constant f beyond boundary + if bc == "constant" + begin_s_r_vperp_vpa_region() + density_offset = 1.0 + vwidth = 1.0 + if z.irank == 0 + @loop_s_r_vperp_vpa is ir ivperp ivpa begin + if adv[is].speed[ivpa,1,ir] > 0.0 + pdf[ivpa,ivperp,1,ir,is] = density_offset * exp(-(vpa.grid[ivpa]^2 + vperp.grid[ivperp]^2)/vwidth^2) / sqrt(pi) + end + end + end + if z.irank == z.nrank - 1 + @loop_s_r_vperp_vpa is ir ivperp ivpa begin + if adv[is].speed[ivpa,end,ir] > 0.0 + pdf[ivpa,ivperp,end,ir,is] = density_offset * exp(-(vpa.grid[ivpa]^2 + vperp.grid[ivperp]^2)/vwidth^2) / sqrt(pi) + end + end + end + # 'periodic' BC enforces periodicity by taking the average of the boundary points + elseif bc == "periodic" && z.nelement_global == z.nelement_local + begin_s_r_vperp_vpa_region() + @loop_s_r_vperp_vpa is ir ivperp ivpa begin + pdf[ivpa,ivperp,1,ir,is] = 0.5*(pdf[ivpa,ivperp,z.n,ir,is]+pdf[ivpa,ivperp,1,ir,is]) + pdf[ivpa,ivperp,z.n,ir,is] = pdf[ivpa,ivperp,1,ir,is] + end + # 'wall' BC enforces wall boundary conditions + elseif bc == "wall" + # Need integrals over vpa at wall boundaries in z, so cannot parallelize over z + # or vpa. + begin_s_r_region() + @loop_s is begin + # zero incoming BC for ions, as they recombine at the wall + if moments.evolve_upar + @loop_r ir begin + @views enforce_zero_incoming_bc!( + pdf[:,:,:,ir,is], z, vpa, density[:,ir,is], upar[:,ir,is], + ppar[:,ir,is], moments.evolve_upar, moments.evolve_ppar, zero) + end + else + @loop_r ir begin + @views enforce_zero_incoming_bc!(pdf[:,:,:,ir,is], + adv[is].speed[:,:,:,ir], z, zero) + end + end + end + end +end + +""" +enforce boundary conditions on neutral particle distribution function +""" +function enforce_neutral_boundary_conditions!(f_neutral, f_ion, + boundary_distributions, density_neutral, uz_neutral, pz_neutral, moments, + density_ion, upar_ion, Er, vzeta_spectral, vr_spectral, vz_spectral, r_adv, z_adv, + vzeta_adv, vr_adv, vz_adv, r, z, vzeta, vr, vz, composition, geometry, + scratch_dummy, r_diffusion, vz_diffusion) + + # without acceleration of neutrals bc on vz vr vzeta should not be required as no + # advection or diffusion in these coordinates + + if vzeta_adv !== nothing && vzeta.n_global > 1 && vzeta.bc != "none" + begin_sn_r_z_vr_vz_region() + @loop_sn_r_z_vr_vz isn ir iz ivr ivz begin + # enforce the vz BC + @views enforce_v_boundary_condition_local!(f_neutral[ivz,ivr,:,iz,ir,isn], + vzeta.bc, + vzeta_adv[isn].speed[ivz,ivr,:,iz,ir], + false, vzeta, vzeta_spectral) + end + end + if vr_adv !== nothing && vr.n_global > 1 && vr.bc != "none" + begin_sn_r_z_vzeta_vz_region() + @loop_sn_r_z_vzeta_vz isn ir iz ivzeta ivz begin + # enforce the vz BC + @views enforce_v_boundary_condition_local!(f_neutral[ivz,:,ivzeta,iz,ir,isn], + vr.bc, + vr_adv[isn].speed[ivz,:,ivzeta,iz,ir], + false, vr, vr_spectral) + end + end + if vz_adv !== nothing && vz.n_global > 1 && vz.bc != "none" + begin_sn_r_z_vzeta_vr_region() + @loop_sn_r_z_vzeta_vr isn ir iz ivzeta ivr begin + # enforce the vz BC + @views enforce_v_boundary_condition_local!(f_neutral[:,ivr,ivzeta,iz,ir,isn], + vz.bc, + vz_adv[isn].speed[:,ivr,ivzeta,iz,ir], + vz_diffusion, vz, vz_spectral) + end + end + # f_initial contains the initial condition for enforcing a fixed-boundary-value condition + if z.n > 1 + begin_sn_r_vzeta_vr_vz_region() + enforce_neutral_z_boundary_condition!(f_neutral, density_neutral, uz_neutral, + pz_neutral, moments, density_ion, upar_ion, Er, boundary_distributions, + z_adv, z, vzeta, vr, vz, composition, geometry, + scratch_dummy.buffer_vzvrvzetarsn_1, scratch_dummy.buffer_vzvrvzetarsn_2, + scratch_dummy.buffer_vzvrvzetarsn_3, scratch_dummy.buffer_vzvrvzetarsn_4) + end + if r.n > 1 + begin_sn_z_vzeta_vr_vz_region() + enforce_neutral_r_boundary_condition!(f_neutral, boundary_distributions.pdf_rboundary_neutral, + r_adv, vz, vr, vzeta, z, r, composition, + scratch_dummy.buffer_vzvrvzetazsn_1, scratch_dummy.buffer_vzvrvzetazsn_2, + scratch_dummy.buffer_vzvrvzetazsn_3, scratch_dummy.buffer_vzvrvzetazsn_4, + r_diffusion) + end +end + +function enforce_neutral_r_boundary_condition!(f::AbstractArray{mk_float,6}, + f_r_bc::AbstractArray{mk_float,6}, adv, vz, vr, vzeta, z, r, composition, + end1::AbstractArray{mk_float,5}, end2::AbstractArray{mk_float,5}, + buffer1::AbstractArray{mk_float,5}, buffer2::AbstractArray{mk_float,5}, + r_diffusion) #f_initial, + + bc = r.bc + nr = r.n + + if r.nelement_global > r.nelement_local + # reconcile internal element boundaries across processes + # & enforce periodicity and external boundaries if needed + @loop_sn_z_vzeta_vr_vz isn iz ivzeta ivr ivz begin + end1[ivz,ivr,ivzeta,iz,isn] = f[ivz,ivr,ivzeta,iz,1,isn] + end2[ivz,ivr,ivzeta,iz,isn] = f[ivz,ivr,ivzeta,iz,nr,isn] + end + reconcile_element_boundaries_MPI!(f, end1, end2, buffer1, buffer2, r) + end + # 'periodic' BC enforces periodicity by taking the average of the boundary points + # local case only when no communication required + if bc == "periodic" && r.nelement_global == r.nelement_local + @loop_sn_z_vzeta_vr_vz isn iz ivzeta ivr ivz begin + f[ivz,ivr,ivzeta,iz,1,isn] = 0.5*(f[ivz,ivr,ivzeta,iz,1,isn]+f[ivz,ivr,ivzeta,iz,nr,isn]) + f[ivz,ivr,ivzeta,iz,nr,isn] = f[ivz,ivr,ivzeta,iz,1,isn] + end + end + # Dirichlet boundary condition for external endpoints + if bc == "Dirichlet" + zero = 1.0e-10 + # use the old distribution to force the new distribution to have + # consistant-in-time values at the boundary + # impose bc for incoming parts of velocity space only (Hyperbolic PDE) + @loop_sn_z_vzeta_vr_vz isn iz ivzeta ivr ivz begin + ir = 1 # r = -L/2 + # incoming particles and on lowest rank + if r.irank == 0 && (r_diffusion || adv[isn].speed[ir,ivz,ivr,ivzeta,iz] > zero) + f[ivz,ivr,ivzeta,iz,ir,isn] = f_r_bc[ivz,ivr,ivzeta,iz,1,isn] + end + ir = nr # r = L/2 + # incoming particles and on highest rank + if r.irank == r.nrank - 1 && (r_diffusion || adv[isn].speed[ir,ivz,ivr,ivzeta,iz] < -zero) + f[ivz,ivr,ivzeta,iz,ir,isn] = f_r_bc[ivz,ivr,ivzeta,iz,end,isn] + end + end + end +end + +""" +enforce boundary conditions on neutral particle f in z +""" +function enforce_neutral_z_boundary_condition!(pdf, density, uz, pz, moments, density_ion, + upar_ion, Er, boundary_distributions, adv, + z, vzeta, vr, vz, composition, geometry, + end1::AbstractArray{mk_float,5}, end2::AbstractArray{mk_float,5}, + buffer1::AbstractArray{mk_float,5}, buffer2::AbstractArray{mk_float,5}) + + + if z.nelement_global > z.nelement_local + # reconcile internal element boundaries across processes + # & enforce periodicity and external boundaries if needed + nz = z.n + @loop_sn_r_vzeta_vr_vz isn ir ivzeta ivr ivz begin + end1[ivz,ivr,ivzeta,ir,isn] = pdf[ivz,ivr,ivzeta,1,ir,isn] + end2[ivz,ivr,ivzeta,ir,isn] = pdf[ivz,ivr,ivzeta,nz,ir,isn] + end + # check on periodic bc occurs within this call below + reconcile_element_boundaries_MPI!(pdf, end1, end2, buffer1, buffer2, z) + end + + zero = 1.0e-14 + # 'constant' BC is time-independent f at upwind boundary + # and constant f beyond boundary + if z.bc == "constant" + begin_sn_r_vzeta_vr_vz_region() + density_offset = 1.0 + vwidth = 1.0 + if z.irank == 0 + @loop_sn_r_vzeta_vr_vz isn ir ivzeta ivr ivz begin + if adv[isn].speed[ivz,ivr,ivzeta,1,ir] > 0.0 + pdf[ivz,ivr,ivzeta,1,ir,is] = density_offset * + exp(-(vzeta.grid[ivzeta]^2 + vr.grid[ivr] + vz.grid[ivz])/vwidth^2) / + sqrt(pi) + end + end + end + if z.irank == z.nrank - 1 + @loop_sn_r_vzeta_vr_vz isn ir ivzeta ivr ivz begin + if adv[isn].speed[ivz,ivr,ivzeta,end,ir] > 0.0 + pdf[ivz,ivr,ivzeta,end,ir,is] = density_offset * + exp(-(vzeta.grid[ivzeta]^2 + vr.grid[ivr] + vz.grid[ivz])/vwidth^2) / + sqrt(pi) + end + end + end + # 'periodic' BC enforces periodicity by taking the average of the boundary points + elseif z.bc == "periodic" && z.nelement_global == z.nelement_local + begin_sn_r_vzeta_vr_vz_region() + @loop_sn_r_vzeta_vr_vz isn ir ivzeta ivr ivz begin + pdf[ivz,ivr,ivzeta,1,ir,isn] = 0.5*(pdf[ivz,ivr,ivzeta,1,ir,isn] + + pdf[ivz,ivr,ivzeta,end,ir,isn]) + pdf[ivz,ivr,ivzeta,end,ir,isn] = pdf[ivz,ivr,ivzeta,1,ir,isn] + end + # 'wall' BC enforces wall boundary conditions + elseif z.bc == "wall" + # Need integrals over vpa at wall boundaries in z, so cannot parallelize over z + # or vpa. + begin_sn_r_region() + @loop_sn isn begin + # BC for neutrals + @loop_r ir begin + # define vtfac to avoid repeated computation below + vtfac = sqrt(composition.T_wall * composition.mn_over_mi) + # Assume for now that the ion species index corresponding to this neutral + # species is the same as the neutral species index. + # Note, have already calculated moments of ion distribution function(s), + # so can use the moments here to get the flux + if z.irank == 0 + ion_flux_0 = -density_ion[1,ir,isn] * (upar_ion[1,ir,isn]*geometry.bzed[1,ir] - 0.5*geometry.rhostar*Er[1,ir]) + else + ion_flux_0 = NaN + end + if z.irank == z.nrank - 1 + ion_flux_L = density_ion[end,ir,isn] * (upar_ion[end,ir,isn]*geometry.bzed[end,ir] - 0.5*geometry.rhostar*Er[end,ir]) + else + ion_flux_L = NaN + end + # enforce boundary condition on the neutral pdf that all ions and neutrals + # that leave the domain re-enter as neutrals + @views enforce_neutral_wall_bc!( + pdf[:,:,:,:,ir,isn], z, vzeta, vr, vz, pz[:,ir,isn], uz[:,ir,isn], + density[:,ir,isn], ion_flux_0, ion_flux_L, boundary_distributions, + vtfac, composition.recycling_fraction, moments.evolve_ppar, + moments.evolve_upar, moments.evolve_density, zero) + end + end + end +end + +""" +enforce a zero incoming BC in z for given species pdf at each radial location +""" +function enforce_zero_incoming_bc!(pdf, speed, z, zero) + nvpa = size(pdf,1) + # no parallel BC should be enforced for dz/dt = 0 + # note that the parallel velocity coordinate vpa may be dz/dt or + # some version of the peculiar velocity (dz/dt - upar), + # so use advection speed below instead of vpa + if z.irank == 0 + @loop_vperp_vpa ivperp ivpa begin + # for left boundary in zed (z = -Lz/2), want + # f(z=-Lz/2, v_parallel > 0) = 0 + if speed[1,ivpa,ivperp] > zero + pdf[ivpa,ivperp,1] = 0.0 + end + end + end + if z.irank == z.nrank - 1 + @loop_vperp_vpa ivperp ivpa begin + # for right boundary in zed (z = Lz/2), want + # f(z=Lz/2, v_parallel < 0) = 0 + if speed[end,ivpa,ivperp] < -zero + pdf[ivpa,ivperp,end] = 0.0 + end + end + end +end +function enforce_zero_incoming_bc!(pdf, z::coordinate, vpa::coordinate, density, upar, + ppar, evolve_upar, evolve_ppar, zero) + if z.irank != 0 && z.irank != z.nrank - 1 + # No z-boundary in this block + return nothing + end + nvpa, nvperp, nz = size(pdf) + # no parallel BC should be enforced for dz/dt = 0 + # note that the parallel velocity coordinate vpa may be dz/dt or + # some version of the peculiar velocity (dz/dt - upar), + # so use advection speed below instead of vpa + + # absolute velocity at left boundary + if z.irank == 0 + @. vpa.scratch = vpagrid_to_dzdt(vpa.grid, sqrt(2.0*(ppar[1]/density[1])), + upar[1], evolve_ppar, evolve_upar) + @loop_vpa ivpa begin + # for left boundary in zed (z = -Lz/2), want + # f(z=-Lz/2, v_parallel > 0) = 0 + if vpa.scratch[ivpa] > zero + pdf[ivpa,:,1] .= 0.0 + end + end + end + # absolute velocity at right boundary + if z.irank == z.nrank - 1 + @. vpa.scratch2 = vpagrid_to_dzdt(vpa.grid, sqrt(2.0*(ppar[end]/density[end])), + upar[end], evolve_ppar, evolve_upar) + @loop_vpa ivpa begin + # for right boundary in zed (z = Lz/2), want + # f(z=Lz/2, v_parallel < 0) = 0 + if vpa.scratch2[ivpa] < -zero + pdf[ivpa,:,end] .= 0.0 + end + end + end + + # Special constraint-forcing code that tries to keep the modifications smooth at + # v_parallel=0. + if z.irank == 0 && z.irank == z.nrank - 1 + # Both z-boundaries in this block + z_range = (1,nz) + elseif z.irank == 0 + z_range = (1,) + elseif z.irank == z.nrank - 1 + z_range = (nz,) + else + error("No boundary in this block, should have returned already") + end + for iz ∈ z_range + # moment-kinetic approach only implemented for 1V case so far + @boundscheck size(pdf,2) == 1 + + f = @view pdf[:,1,iz] + if evolve_ppar && evolve_upar + I0 = integrate_over_vspace(f, vpa.wgts) + I1 = integrate_over_vspace(f, vpa.grid, vpa.wgts) + I2 = integrate_over_vspace(f, vpa.grid, 2, vpa.wgts) + + # Store v_parallel with upar shift removed in vpa.scratch + vth = sqrt(2.0*ppar[iz]/density[iz]) + @. vpa.scratch = vpa.grid + upar[iz]/vth + # Introduce factor to ensure corrections go smoothly to zero near + # v_parallel=0 + @. vpa.scratch2 = f * abs(vpa.scratch) / (1.0 + abs(vpa.scratch)) + J1 = integrate_over_vspace(vpa.scratch2, vpa.grid, vpa.wgts) + J2 = integrate_over_vspace(vpa.scratch2, vpa.grid, 2, vpa.wgts) + J3 = integrate_over_vspace(vpa.scratch2, vpa.grid, 3, vpa.wgts) + J4 = integrate_over_vspace(vpa.scratch2, vpa.grid, 4, vpa.wgts) + + A = (J3^2 - J2*J4 + 0.5*(J2^2 - J1*J3)) / + (I0*(J3^2 - J2*J4) + I1*(J1*J4 - J2*J3) + I2*(J2^2 - J1*J3)) + B = (0.5*J3 + A*(I1*J4 - I2*J3)) / (J3^2 - J2*J4) + C = (0.5 - A*I2 -B*J3) / J4 + + @. f = A*f + B*vpa.grid*vpa.scratch2 + C*vpa.grid*vpa.grid*vpa.scratch2 + elseif evolve_upar + I0 = integrate_over_vspace(f, vpa.wgts) + I1 = integrate_over_vspace(f, vpa.grid, vpa.wgts) + + # Store v_parallel with upar shift removed in vpa.scratch + @. vpa.scratch = vpa.grid + upar[iz] + # Introduce factor to ensure corrections go smoothly to zero near + # v_parallel=0 + @. vpa.scratch2 = f * abs(vpa.scratch) / (1.0 + abs(vpa.scratch)) + J1 = integrate_over_vspace(vpa.scratch2, vpa.grid, vpa.wgts) + J2 = integrate_over_vspace(vpa.scratch2, vpa.grid, 2, vpa.wgts) + + A = 1.0 / (I0 - I1*J1/J2) + B = -A*I1/J2 + + @. f = A*f + B*vpa.grid*vpa.scratch2 + elseif evolve_density + I0 = integrate_over_vspace(f, vpa.wgts) + @. f = f / I0 + end + end +end + +""" +Set up an initial condition that tries to be smoothly compatible with the sheath +boundary condition for ions, by setting f(±(v_parallel-u0)<0) where u0=0 at the sheath +boundaries and for z<0 increases linearly to u0=vpa.L at z=0, while for z>0 increases +from u0=-vpa.L at z=0 to zero at the z=z.L/2 sheath. + +To be applied to 'full-f' distribution function on v_parallel grid (not w_parallel +grid). +""" +function enforce_initial_tapered_zero_incoming!(pdf, z::coordinate, vpa::coordinate) + nvpa = size(pdf,1) + zero = 1.0e-14 + # no parallel BC should be enforced for dz/dt = 0 + # note that the parallel velocity coordinate vpa may be dz/dt or + # some version of the peculiar velocity (dz/dt - upar), + # so use advection speed below instead of vpa + + for iz ∈ 1:z.n + u0 = (2.0*z.grid[iz]/z.L - sign(z.grid[iz])) * vpa.L / 2.0 + if z.grid[iz] < -zero + for ivpa ∈ 1:nvpa + if vpa.grid[ivpa] > u0 + zero + pdf[ivpa,iz] = 0.0 + end + end + elseif z.grid[iz] > zero + for ivpa ∈ 1:nvpa + if vpa.grid[ivpa] < u0 - zero + pdf[ivpa,iz] = 0.0 + end + end + end + end +end + +""" +enforce the wall boundary condition on neutrals; +i.e., the incoming flux of neutrals equals the sum of the ion/neutral outgoing fluxes +""" +function enforce_neutral_wall_bc!(pdf, z, vzeta, vr, vz, pz, uz, density, wall_flux_0, + wall_flux_L, boundary_distributions, vtfac, + recycling_fraction, evolve_ppar, evolve_upar, + evolve_density, zero) + + # Reduce the ion flux by `recycling_fraction` to account for ions absorbed by the + # wall. + wall_flux_0 *= recycling_fraction + wall_flux_L *= recycling_fraction + + if !evolve_density && !evolve_upar + knudsen_cosine = boundary_distributions.knudsen + + if z.irank == 0 + ## treat z = -Lz/2 boundary ## + + # add the neutral species's contribution to the combined ion/neutral particle + # flux out of the domain at z=-Lz/2 + @views wall_flux_0 += integrate_over_negative_vz(abs.(vz.grid) .* pdf[:,:,:,1], vz.grid, vz.wgts, vz.scratch, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) + + # for left boundary in zed (z = -Lz/2), want + # f_n(z=-Lz/2, v_parallel > 0) = Γ_0 * f_KW(v_parallel) + @loop_vz ivz begin + if vz.grid[ivz] >= -zero + @views @. pdf[ivz,:,:,1] = wall_flux_0 * knudsen_cosine[ivz,:,:] + end + end + end + + if z.irank == z.nrank - 1 + ## treat the right boundary at z = Lz/2 ## + + # add the neutral species's contribution to the combined ion/neutral particle + # flux out of the domain at z=-Lz/2 + @views wall_flux_L += integrate_over_positive_vz(abs.(vz.grid) .* pdf[:,:,:,end], vz.grid, vz.wgts, vz.scratch, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) + + # for right boundary in zed (z = Lz/2), want + # f_n(z=Lz/2, v_parallel < 0) = Γ_Lz * f_KW(v_parallel) + @loop_vz ivz begin + if vz.grid[ivz] <= zero + @views @. pdf[ivz,:,:,end] = wall_flux_L * knudsen_cosine[ivz,:,:] + end + end + end + elseif !evolve_upar + # Evolving density case + knudsen_cosine = boundary_distributions.knudsen + + if z.irank == 0 + ## treat z = -Lz/2 boundary ## + + # Note the numerical integrol of knudsen_cosine was forced to be 1 (to machine + # precision) when it was initialised. + @views pdf_integral_0 = integrate_over_negative_vz(pdf[:,:,:,1], vz.grid, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) + @views pdf_integral_1 = integrate_over_negative_vz(vz.grid .* pdf[:,:,:,1], vz.grid, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) + knudsen_integral_0 = integrate_over_positive_vz(knudsen_cosine, vz.grid, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) + knudsen_integral_1 = 1.0 # This is enforced in initialization + + # Calculate normalisation factors N_in for the incoming and N_out for the + # Knudsen parts of the distirbution so that ∫dvpa F = 1 and ∫dvpa vpa F = uz + # Note wall_flux_0 is the ion flux into the wall (reduced by the recycling + # fraction), and the neutral flux should be out of the wall (i.e. uz>0) so + # n*uz = |n*uz| = wall_flux_0 + # ⇒ N_in*pdf_integral_0 + N_out*knudsen_integral_0 = 1 + # N_in*pdf_integral_1 + N_out*knudsen_integral_1 = uz + uz = wall_flux_0 / density[1] + N_in = (1 - uz * knudsen_integral_0 / knudsen_integral_1) / + (pdf_integral_0 + - pdf_integral_1 / knudsen_integral_1 * knudsen_integral_0) + N_out = (uz - N_in * pdf_integral_1) / knudsen_integral_1 + + @loop_vz ivz begin + if vz.grid[ivz] >= -zero + @views @. pdf[ivz,:,:,1] = N_out * knudsen_cosine[ivz,:,:] + else + @views @. pdf[ivz,:,:,1] *= N_in + end + end + end + + if z.irank == z.nrank - 1 + ## treat the right boundary at z = Lz/2 ## + + # Note the numerical integrol of knudsen_cosine was forced to be 1 (to machine + # precision) when it was initialised. + @views pdf_integral_0 = integrate_over_positive_vz(pdf[:,:,:,end], vz.grid, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) + @views pdf_integral_1 = integrate_over_positive_vz(vz.grid .* pdf[:,:,:,end], vz.grid, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) + knudsen_integral_0 = integrate_over_negative_vz(knudsen_cosine, vz.grid, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) + knudsen_integral_1 = -1.0 # This is enforced in initialization + + # Calculate normalisation factors N_in for the incoming and N_out for the + # Knudsen parts of the distirbution so that ∫dvpa F = 1 and ∫dvpa vpa F = uz + # Note wall_flux_L is the ion flux into the wall (reduced by the recycling + # fraction), and the neutral flux should be out of the wall (i.e. uz<0) so + # -n*uz = |n*uz| = wall_flux_L + # ⇒ N_in*pdf_integral_0 + N_out*knudsen_integral_0 = 1 + # N_in*pdf_integral_1 + N_out*knudsen_integral_1 = uz + uz = -wall_flux_L / density[end] + N_in = (1 - uz * knudsen_integral_0 / knudsen_integral_1) / + (pdf_integral_0 + - pdf_integral_1 / knudsen_integral_1 * knudsen_integral_0) + N_out = (uz - N_in * pdf_integral_1) / knudsen_integral_1 + + @loop_vz ivz begin + if vz.grid[ivz] <= zero + @views @. pdf[ivz,:,:,end] = N_out * knudsen_cosine[ivz,:,:] + else + @views @. pdf[ivz,:,:,end] *= N_in + end + end + end + else + if z.irank == 0 + ## treat z = -Lz/2 boundary ## + # populate vz.scratch2 array with dz/dt values at z = -Lz/2 + if evolve_ppar + vth = sqrt(2.0*pz[1]/density[1]) + else + vth = nothing + end + @. vz.scratch2 = vpagrid_to_dzdt(vz.grid, vth, uz[1], evolve_ppar, evolve_upar) + + # First apply boundary condition that total neutral outflux is equal to ion + # influx to uz + uz[1] = wall_flux_0 / density[1] + #would setting density work better?? + #density[1] = - wall_flux_0 / uz[1] + + # Create normalised Knudsen cosine distribution, to use for positive v_parallel + # at z = -Lz/2 + # Note this only makes sense for the 1V case with vr.n=vzeta.n=1 + @. vz.scratch = (3.0*pi/vtfac^3)*abs(vz.scratch2)*erfc(abs(vz.scratch2)/vtfac) + + # The v_parallel>0 part of the pdf is replaced by the Knudsen cosine + # distribution. To ensure the constraints ∫dwpa wpa^m F = 0 are satisfied when + # necessary, calculate a normalisation factor for the Knudsen distribution (in + # vz.scratch) and correction terms for the incoming pdf similar to + # enforce_moment_constraints!(). + # + # Note that it seems to be important that this boundary condition not be + # modified by the moment constraints, as that could cause numerical instability. + # By ensuring that the constraints are satisfied already here, + # enforce_moment_constraints!() will not change the pdf at the boundary. For + # ions this is not an issue, because points set to 0 by the bc are not modified + # from 0 by enforce_moment_constraints!(). + knudsen_integral_0 = integrate_over_positive_vz(vz.scratch, vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) + knudsen_integral_1 = integrate_over_positive_vz(vz.grid .* vz.scratch, vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) + + @views pdf_integral_0 = integrate_over_negative_vz(pdf[:,:,:,1], vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) + @views pdf_integral_1 = integrate_over_negative_vz(vz.grid .* pdf[:,:,:,1], vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) + if !evolve_ppar + # Calculate normalisation factors N_in for the incoming and N_out for the + # Knudsen parts of the distirbution so that ∫dwpa F = 1 and ∫dwpa wpa F = 0 + # ⇒ N_in*pdf_integral_0 + N_out*knudsen_integral_0 = 1 + # N_in*pdf_integral_1 + N_out*knudsen_integral_1 = 0 + N_in = 1.0 / (pdf_integral_0 - pdf_integral_1/knudsen_integral_1*knudsen_integral_0) + N_out = -N_in * pdf_integral_1 / knudsen_integral_1 + + zero_vz_ind = 0 + for ivz ∈ 1:vz.n + if vz.scratch2[ivz] <= -zero + pdf[ivz,:,:,1] .= N_in*pdf[ivz,:,:,1] + else + zero_vz_ind = ivz + if abs(vz.scratch2[ivz]) < zero + # v_z = 0 point, half contribution from original pdf and half + # from Knudsen cosine distribution, to be consistent with weights + # used in + # integrate_over_positive_vz()/integrate_over_negative_vz(). + @. pdf[ivz,:,:,1] = 0.5*(N_in*pdf[ivz,:,:,1] + N_out*vz.scratch[ivz]) + else + pdf[ivz,:,:,1] .= N_out*vz.scratch[ivz] + end + break + end + end + for ivz ∈ zero_vz_ind+1:vz.n + pdf[ivz,:,:,1] .= N_out*vz.scratch[ivz] + end + else + knudsen_integral_2 = integrate_over_positive_vz(vz.grid .* vz.grid .* vz.scratch, vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) + @views pdf_integral_2 = integrate_over_negative_vz(vz.grid .* vz.grid .* pdf[:,:,:,1], vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) + @views pdf_integral_3 = integrate_over_negative_vz(vz.grid .* vz.grid .* vz.grid .* pdf[:,:,:,1], vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) + # Calculate normalisation factor N_out for the Knudsen part of the + # distirbution and normalisation factor N_in and correction term C*wpa*F_in + # for the incoming distribution so that ∫dwpa F = 1, ∫dwpa wpa F = 0, and + # ∫dwpa wpa^2 F = 1/2 + # ⇒ N_in*pdf_integral_0 + C*pdf_integral_1 + N_out*knudsen_integral_0 = 1 + # N_in*pdf_integral_1 + C*pdf_integral_2 + N_out*knudsen_integral_1 = 0 + # N_in*pdf_integral_2 + C*pdf_integral_3 + N_out*knudsen_integral_2 = 1/2 + N_in = (0.5*knudsen_integral_0*pdf_integral_2 + + knudsen_integral_1*(pdf_integral_3 - 0.5*pdf_integral_1) - + knudsen_integral_2*pdf_integral_2) / + (knudsen_integral_0*(pdf_integral_2^2 - pdf_integral_1*pdf_integral_3) + + knudsen_integral_1*(pdf_integral_0*pdf_integral_3 - pdf_integral_1*pdf_integral_2) + + knudsen_integral_2*(pdf_integral_1^2 - pdf_integral_0*pdf_integral_2)) + N_out = -(N_in*(pdf_integral_1*pdf_integral_3 - pdf_integral_2^2) + 0.5*pdf_integral_2) / + (knudsen_integral_1*pdf_integral_3 - knudsen_integral_2*pdf_integral_2) + C = (0.5 - N_out*knudsen_integral_2 - N_in*pdf_integral_2)/pdf_integral_3 + + zero_vz_ind = 0 + for ivz ∈ 1:vz.n + if vz.scratch2[ivz] <= -zero + @views @. pdf[ivz,:,:,1] = N_in*pdf[ivz,:,:,1] + C*vz.grid[ivz]*pdf[ivz,:,:,1] + else + zero_vz_ind = ivz + if abs(vz.scratch2[ivz]) < zero + # v_parallel = 0 point, half contribution from original pdf and half + # from Knudsen cosine distribution, to be consistent with weights + # used in + # integrate_over_positive_vz()/integrate_over_negative_vz(). + @views @. pdf[ivz,:,:,1] = 0.5*(N_in*pdf[ivz,:,:,1] + + C*vz.grid[ivz]*pdf[ivz,:,:,1] + + N_out*vz.scratch[ivz]) + else + @. pdf[ivz,:,:,1] = N_out*vz.scratch[ivz] + end + break + end + end + for ivz ∈ zero_vz_ind+1:vz.n + @. pdf[ivz,:,:,1] = N_out*vz.scratch[ivz] + end + end + end + + if z.irank == z.nrank - 1 + ## treat the right boundary at z = Lz/2 ## + # populate vz.scratch2 array with dz/dt values at z = Lz/2 + if evolve_ppar + vth = sqrt(2.0*pz[end]/density[end]) + else + vth = nothing + end + @. vz.scratch2 = vpagrid_to_dzdt(vz.grid, vth, uz[end], evolve_ppar, evolve_upar) + + # First apply boundary condition that total neutral outflux is equal to ion + # influx to uz + uz[end] = - wall_flux_L / density[end] + #would setting density work better?? + #density[end] = - wall_flux_L / upar[end] + + # obtain the Knudsen cosine distribution at z = Lz/2 + # the z-dependence is only introduced if the peculiiar velocity is used as vz + # Note this only makes sense for the 1V case with vr.n=vzeta.n=1 + @. vz.scratch = (3.0*pi/vtfac^3)*abs(vz.scratch2)*erfc(abs(vz.scratch2)/vtfac) + + # The v_parallel<0 part of the pdf is replaced by the Knudsen cosine + # distribution. To ensure the constraint ∫dwpa wpa F = 0 is satisfied, multiply + # the Knudsen distribution (in vz.scratch) by a normalisation factor given by + # the integral (over negative v_parallel) of the outgoing Knudsen distribution + # and (over positive v_parallel) of the incoming pdf. + knudsen_integral_0 = integrate_over_negative_vz(vz.scratch, vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) + knudsen_integral_1 = integrate_over_negative_vz(vz.grid .* vz.scratch, vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) + + @views pdf_integral_0 = integrate_over_positive_vz(pdf[:,:,:,end], vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) + @views pdf_integral_1 = integrate_over_positive_vz(vz.grid .* pdf[:,:,:,end], vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) + + if !evolve_ppar + # Calculate normalisation factors N_in for the incoming and N_out for the + # Knudsen parts of the distirbution so that ∫dwpa F = 1 and ∫dwpa wpa F = 0 + # ⇒ N_in*pdf_integral_0 + N_out*knudsen_integral_0 = 1 + # N_in*pdf_integral_1 + N_out*knudsen_integral_1 = 0 + N_in = 1.0 / (pdf_integral_0 - pdf_integral_1/knudsen_integral_1*knudsen_integral_0) + N_out = -N_in * pdf_integral_1 / knudsen_integral_1 + + zero_vz_ind = 0 + for ivz ∈ vz.n:-1:1 + if vz.scratch2[ivz] >= zero + @views @. pdf[ivz,:,:,end] = N_in*pdf[ivz,:,:,end] + else + zero_vz_ind = ivz + if abs(vz.scratch2[ivz]) < zero + # v_parallel = 0 point, half contribution from original pdf and half + # from Knudsen cosine distribution, to be consistent with weights + # used in + # integrate_over_positive_vz()/integrate_over_negative_vz(). + @views @. pdf[ivz,:,:,end] = 0.5*(N_in*pdf[ivz,:,:,end] + N_out*vz.scratch[ivz]) + else + @. pdf[ivz,:,:,end] = N_out*vz.scratch[ivz] + end + break + end + end + for ivz ∈ 1:zero_vz_ind-1 + @. pdf[ivz,:,:,end] = N_out*vz.scratch[ivz] + end + else + knudsen_integral_2 = integrate_over_negative_vz(vz.grid .* vz.grid .* vz.scratch, vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) + @views pdf_integral_2 = integrate_over_positive_vz(vz.grid .* vz.grid .* pdf[:,:,:,end], vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) + @views pdf_integral_3 = integrate_over_positive_vz(vz.grid .* vz.grid .* vz.grid .* pdf[:,:,:,end], vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) + # Calculate normalisation factor N_out for the Knudsen part of the + # distirbution and normalisation factor N_in and correction term C*wpa*F_in + # for the incoming distribution so that ∫dwpa F = 1, ∫dwpa wpa F = 0, and + # ∫dwpa wpa^2 F = 1/2 + # ⇒ N_in*pdf_integral_0 + C*pdf_integral_1 + N_out*knudsen_integral_0 = 1 + # N_in*pdf_integral_1 + C*pdf_integral_2 + N_out*knudsen_integral_1 = 0 + # N_in*pdf_integral_2 + C*pdf_integral_3 + N_out*knudsen_integral_2 = 1/2 + N_in = (0.5*knudsen_integral_0*pdf_integral_2 + + knudsen_integral_1*(pdf_integral_3 - 0.5*pdf_integral_1) - + knudsen_integral_2*pdf_integral_2) / + (knudsen_integral_0*(pdf_integral_2^2 - pdf_integral_1*pdf_integral_3) + + knudsen_integral_1*(pdf_integral_0*pdf_integral_3 - pdf_integral_1*pdf_integral_2) + + knudsen_integral_2*(pdf_integral_1^2 - pdf_integral_0*pdf_integral_2)) + N_out = -(N_in*(pdf_integral_1*pdf_integral_3 - pdf_integral_2^2) + 0.5*pdf_integral_2) / + (knudsen_integral_1*pdf_integral_3 - knudsen_integral_2*pdf_integral_2) + C = (0.5 - N_out*knudsen_integral_2 - N_in*pdf_integral_2)/pdf_integral_3 + + zero_vz_ind = 0 + for ivz ∈ vz.n:-1:1 + if vz.scratch2[ivz] >= zero + @views @. pdf[ivz,:,:,end] = N_in*pdf[ivz,:,:,end] + C*vz.grid[ivz]*pdf[ivz,:,:,end] + else + zero_vz_ind = ivz + if abs(vz.scratch2[ivz]) < zero + # v_parallel = 0 point, half contribution from original pdf and half + # from Knudsen cosine distribution, to be consistent with weights + # used in + # integrate_over_positive_vz()/integrate_over_negative_vz(). + @views @. pdf[ivz,:,:,end] = 0.5*(N_in*pdf[ivz,:,:,end] + + C*vz.grid[ivz]*pdf[ivz,:,:,end] + + N_out*vz.scratch[ivz]) + else + @. pdf[ivz,:,:,end] = N_out*vz.scratch[ivz] + end + break + end + end + for ivz ∈ 1:zero_vz_ind-1 + @. pdf[ivz,:,:,end] = N_out*vz.scratch[ivz] + end + end + end + end +end + +""" +create an array of dz/dt values corresponding to the given vpagrid values +""" +function vpagrid_to_dzdt(vpagrid, vth, upar, evolve_ppar, evolve_upar) + if evolve_ppar + if evolve_upar + return vpagrid .* vth .+ upar + else + return vpagrid .* vth + end + elseif evolve_upar + return vpagrid .+ upar + else + return vpagrid + end +end + +""" +enforce the z boundary condition on the evolved velocity space moments of f +""" +function enforce_z_boundary_condition_moments!(density, moments, bc::String) + ## TODO: parallelise + #begin_serial_region() + #@serial_region begin + # # enforce z boundary condition on density if it is evolved separately from f + # if moments.evolve_density + # # TODO: extend to 'periodic' BC case, as this requires further code modifications to be consistent + # # with finite difference derivatives (should be fine for Chebyshev) + # if bc == "wall" + # @loop_s_r is ir begin + # density[1,ir,is] = 0.5*(density[1,ir,is] + density[end,ir,is]) + # density[end,ir,is] = density[1,ir,is] + # end + # end + # end + #end +end + +""" +""" +function enforce_v_boundary_condition_local!(f, bc, speed, v_diffusion, v, v_spectral) + if bc == "zero" + if v_diffusion || speed[1] > 0.0 + # 'upwind' boundary + f[1] = 0.0 + end + if v_diffusion || speed[end] < 0.0 + # 'upwind' boundary + f[end] = 0.0 + end + elseif bc == "both_zero" + f[1] = 0.0 + f[end] = 0.0 + elseif bc == "zero_gradient" + D0 = v_spectral.lobatto.Dmat[1,:] + # adjust F(vpa = -L/2) so that d F / d vpa = 0 at vpa = -L/2 + f[1] = -sum(D0[2:v.ngrid].*f[2:v.ngrid])/D0[1] + + D0 = v_spectral.lobatto.Dmat[end,:] + # adjust F(vpa = L/2) so that d F / d vpa = 0 at vpa = L/2 + f[end] = -sum(D0[1:ngrid-1].*f[end-v.ngrid+1:end-1])/D0[v.ngrid] + elseif bc == "periodic" + f[1] = 0.5*(f[1]+f[end]) + f[end] = f[1] + else + error("Unsupported boundary condition option '$bc' for $(v.name)") + end +end + +""" +enforce zero boundary condition at vperp -> infinity +""" +function enforce_vperp_boundary_condition! end + +function enforce_vperp_boundary_condition!(f::AbstractArray{mk_float,5}, bc, vperp, vperp_spectral, vperp_advect, diffusion) + @loop_s is begin + @views enforce_vperp_boundary_condition!(f[:,:,:,:,is], bc, vperp, vperp_spectral, vperp_advect[is], diffusion) + end + return nothing +end + +function enforce_vperp_boundary_condition!(f::AbstractArray{mk_float,4}, bc, vperp, vperp_spectral, vperp_advect, diffusion) + if bc == "zero" + nvperp = vperp.n + ngrid = vperp.ngrid + # set zero boundary condition + @loop_r_z_vpa ir iz ivpa begin + if diffusion || vperp_advect.speed[nvperp,ivpa,iz,ir] < 0.0 + f[ivpa,nvperp,iz,ir] = 0.0 + end + end + # set regularity condition d F / d vperp = 0 at vperp = 0 + if vperp.discretization == "gausslegendre_pseudospectral" || vperp.discretization == "chebyshev_pseudospectral" + D0 = vperp_spectral.radau.D0 + buffer = @view vperp.scratch[1:ngrid-1] + @loop_r_z_vpa ir iz ivpa begin + if diffusion || vperp_advect.speed[1,ivpa,iz,ir] > 0.0 + # adjust F(vperp = 0) so that d F / d vperp = 0 at vperp = 0 + @views @. buffer = D0[2:ngrid] * f[ivpa,2:ngrid,iz,ir] + f[ivpa,1,iz,ir] = -sum(buffer)/D0[1] + end + end + else + println("vperp.bc=\"$bc\" not supported by discretization " + * "$(vperp.discretization)") + end + elseif bc == "none" + # Do nothing + else + error("Unsupported boundary condition option '$bc' for vperp") + end +end + +end # boundary_conditions diff --git a/moment_kinetics/src/calculus.jl b/moment_kinetics/src/calculus.jl index 4809182ed..31f07159b 100644 --- a/moment_kinetics/src/calculus.jl +++ b/moment_kinetics/src/calculus.jl @@ -347,42 +347,42 @@ in the main code function assign_endpoint!(df1d::AbstractArray{mk_float,Ndims}, receive_buffer::AbstractArray{mk_float,Mdims},key::String,coord) where {Ndims,Mdims} - if key == "lower" - j = 1 - elseif key == "upper" - j = coord.n - else - println("ERROR: invalid key in assign_endpoint!") - end + if key == "lower" + j = 1 + elseif key == "upper" + j = coord.n + else + println("ERROR: invalid key in assign_endpoint!") + end # test against coord name -- make sure to use exact string delimiters e.g. "x" not 'x' - # test against Ndims (autodetermined) to choose which array slices to use in assigning endpoints - #println("DEBUG MESSAGE: coord.name: ",coord.name," Ndims: ",Ndims," key: ",key) - if coord.name == "z" && Ndims==2 - df1d[j,:] .= receive_buffer[:] - #println("ASSIGNING DATA") - elseif coord.name == "z" && Ndims==3 - df1d[j,:,:] .= receive_buffer[:,:] - #println("ASSIGNING DATA") - elseif coord.name == "z" && Ndims==5 - df1d[:,:,j,:,:] .= receive_buffer[:,:,:,:] - #println("ASSIGNING DATA") + # test against Ndims (autodetermined) to choose which array slices to use in assigning endpoints + #println("DEBUG MESSAGE: coord.name: ",coord.name," Ndims: ",Ndims," key: ",key) + if coord.name == "z" && Ndims==2 + df1d[j,:] .= receive_buffer[:] + #println("ASSIGNING DATA") + elseif coord.name == "z" && Ndims==3 + df1d[j,:,:] .= receive_buffer[:,:] + #println("ASSIGNING DATA") + elseif coord.name == "z" && Ndims==5 + df1d[:,:,j,:,:] .= receive_buffer[:,:,:,:] + #println("ASSIGNING DATA") elseif coord.name == "z" && Ndims==6 - df1d[:,:,:,j,:,:] .= receive_buffer[:,:,:,:,:] - #println("ASSIGNING DATA") - elseif coord.name == "r" && Ndims==2 - df1d[:,j] .= receive_buffer[:] - #println("ASSIGNING DATA") - elseif coord.name == "r" && Ndims==3 - df1d[:,j,:] .= receive_buffer[:,:] - #println("ASSIGNING DATA") - elseif coord.name == "r" && Ndims==5 - df1d[:,:,:,j,:] .= receive_buffer[:,:,:,:] - #println("ASSIGNING DATA") - elseif coord.name == "r" && Ndims==6 - df1d[:,:,:,:,j,:] .= receive_buffer[:,:,:,:,:] - #println("ASSIGNING DATA") - else - println("ERROR: failure to assign endpoints in reconcile_element_boundaries_MPI! (centered): coord.name: ",coord.name," Ndims: ",Ndims," key: ",key) + df1d[:,:,:,j,:,:] .= receive_buffer[:,:,:,:,:] + #println("ASSIGNING DATA") + elseif coord.name == "r" && Ndims==2 + df1d[:,j] .= receive_buffer[:] + #println("ASSIGNING DATA") + elseif coord.name == "r" && Ndims==3 + df1d[:,j,:] .= receive_buffer[:,:] + #println("ASSIGNING DATA") + elseif coord.name == "r" && Ndims==5 + df1d[:,:,:,j,:] .= receive_buffer[:,:,:,:] + #println("ASSIGNING DATA") + elseif coord.name == "r" && Ndims==6 + df1d[:,:,:,:,j,:] .= receive_buffer[:,:,:,:,:] + #println("ASSIGNING DATA") + else + error("ERROR: failure to assign endpoints in reconcile_element_boundaries_MPI! (centered): coord.name: ",coord.name," Ndims: ",Ndims," key: ",key) end end diff --git a/moment_kinetics/src/charge_exchange.jl b/moment_kinetics/src/charge_exchange.jl index 1e4b5a267..66c1bb7fa 100644 --- a/moment_kinetics/src/charge_exchange.jl +++ b/moment_kinetics/src/charge_exchange.jl @@ -28,7 +28,7 @@ function charge_exchange_collisions_1V!(f_out, f_neutral_out, fvec_in, moments, f_out[:,1,:,:,is], fvec_in.pdf[:,1,:,:,is], fvec_in.pdf_neutral[:,1,1,:,:,is], fvec_in.density_neutral[:,:,is], fvec_in.upar[:,:,is], - fvec_in.uz_neutral[:,:,is], moments.charged.vth[:,:,is], + fvec_in.uz_neutral[:,:,is], moments.ion.vth[:,:,is], moments.neutral.vth[:,:,is], moments, vpa, vz, charge_exchange_frequency, vz_spectral, dt) end @@ -42,7 +42,7 @@ function charge_exchange_collisions_1V!(f_out, f_neutral_out, fvec_in, moments, f_neutral_out[:,1,1,:,:,isn], fvec_in.pdf_neutral[:,1,1,:,:,isn], fvec_in.pdf[:,1,:,:,isn], fvec_in.density[:,:,isn], fvec_in.uz_neutral[:,:,isn], fvec_in.upar[:,:,isn], - moments.neutral.vth[:,:,isn], moments.charged.vth[:,:,isn], moments, + moments.neutral.vth[:,:,isn], moments.ion.vth[:,:,isn], moments, vz, vpa, charge_exchange_frequency, vpa_spectral, dt) end else @@ -135,7 +135,7 @@ function charge_exchange_collisions_single_species!(f_out, pdf_in, pdf_other, end end -function charge_exchange_collisions_3V!(f_out, f_neutral_out, f_neutral_gav_in, f_charged_vrvzvzeta_in, fvec_in, composition, vz, vr, vzeta, vpa, vperp, z, r, +function charge_exchange_collisions_3V!(f_out, f_neutral_out, f_neutral_gav_in, f_ion_vrvzvzeta_in, fvec_in, composition, vz, vr, vzeta, vpa, vperp, z, r, charge_exchange_frequency, dt) # This routine assumes a 3V model with: @boundscheck vz.n == size(f_neutral_out,1) || throw(BoundsError(f_neutral_out)) @@ -144,12 +144,12 @@ function charge_exchange_collisions_3V!(f_out, f_neutral_out, f_neutral_gav_in, @boundscheck z.n == size(f_neutral_out,4) || throw(BoundsError(f_neutral_out)) @boundscheck r.n == size(f_neutral_out,5) || throw(BoundsError(f_neutral_out)) @boundscheck composition.n_neutral_species == size(f_neutral_out,6) || throw(BoundsError(f_neutral_out)) - @boundscheck vz.n == size(f_charged_vrvzvzeta_in,1) || throw(BoundsError(f_charged_vrvzvzeta_in)) - @boundscheck vr.n == size(f_charged_vrvzvzeta_in,2) || throw(BoundsError(f_charged_vrvzvzeta_in)) - @boundscheck vzeta.n == size(f_charged_vrvzvzeta_in,3) || throw(BoundsError(f_charged_vrvzvzeta_in)) - @boundscheck z.n == size(f_charged_vrvzvzeta_in,4) || throw(BoundsError(f_charged_vrvzvzeta_in)) - @boundscheck r.n == size(f_charged_vrvzvzeta_in,5) || throw(BoundsError(f_charged_vrvzvzeta_in)) - @boundscheck composition.n_neutral_species == size(f_charged_vrvzvzeta_in,6) || throw(BoundsError(f_charged_vrvzvzeta_in)) + @boundscheck vz.n == size(f_ion_vrvzvzeta_in,1) || throw(BoundsError(f_ion_vrvzvzeta_in)) + @boundscheck vr.n == size(f_ion_vrvzvzeta_in,2) || throw(BoundsError(f_ion_vrvzvzeta_in)) + @boundscheck vzeta.n == size(f_ion_vrvzvzeta_in,3) || throw(BoundsError(f_ion_vrvzvzeta_in)) + @boundscheck z.n == size(f_ion_vrvzvzeta_in,4) || throw(BoundsError(f_ion_vrvzvzeta_in)) + @boundscheck r.n == size(f_ion_vrvzvzeta_in,5) || throw(BoundsError(f_ion_vrvzvzeta_in)) + @boundscheck composition.n_neutral_species == size(f_ion_vrvzvzeta_in,6) || throw(BoundsError(f_ion_vrvzvzeta_in)) @boundscheck vpa.n == size(f_out,1) || throw(BoundsError(f_out)) @boundscheck vperp.n == size(f_out,2) || throw(BoundsError(f_out)) @boundscheck z.n == size(f_out,3) || throw(BoundsError(f_out)) @@ -181,7 +181,7 @@ function charge_exchange_collisions_3V!(f_out, f_neutral_out, f_neutral_gav_in, for is ∈ 1:composition.n_ion_species f_neutral_out[ivz,ivr,ivzeta,iz,ir,isn] += dt*charge_exchange_frequency*( - f_charged_vrvzvzeta_in[ivz,ivr,ivzeta,iz,ir,is]*fvec_in.density_neutral[iz,ir,isn] + f_ion_vrvzvzeta_in[ivz,ivr,ivzeta,iz,ir,is]*fvec_in.density_neutral[iz,ir,isn] - fvec_in.pdf_neutral[ivz,ivr,ivzeta,iz,ir,isn]*fvec_in.density[iz,ir,is]) end end diff --git a/moment_kinetics/src/communication.jl b/moment_kinetics/src/communication.jl index c43278516..ee1b84f3a 100644 --- a/moment_kinetics/src/communication.jl +++ b/moment_kinetics/src/communication.jl @@ -457,7 +457,7 @@ end A.accessed[] = true return getindex(A.data, I...) end - function Base.setindex!(A::DebugMPISharedArray{T, N}, v::T, I::Vararg{mk_int,N}) where {T, N} + function Base.setindex!(A::DebugMPISharedArray{T, N}, v::Number, I::Vararg{mk_int,N}) where {T, N} @debug_track_initialized begin A.is_initialized[I...] = 1 end @@ -508,12 +508,19 @@ end # SparseArray A import LinearAlgebra: ldiv!, Factorization function ldiv!(Y::DebugMPISharedArray, A::Factorization, B::DebugMPISharedArray) + @debug_track_initialized begin + Y.is_initialized .= 1 + end + Y.is_written .= true + Y.accessed[] = true return ldiv!(Y.data, A, B.data) end import MPI: Buffer function Buffer(A::DebugMPISharedArray) - A.is_initialized .= 1 + @debug_track_initialized begin + A.is_initialized .= 1 + end A.is_read .= true A.is_written .= true A.accessed[] = true diff --git a/moment_kinetics/src/continuity.jl b/moment_kinetics/src/continuity.jl index 80d0183c9..26e9e1314 100644 --- a/moment_kinetics/src/continuity.jl +++ b/moment_kinetics/src/continuity.jl @@ -8,7 +8,7 @@ using ..calculus: derivative! using ..looping """ -use the continuity equation dn/dt + d(n*upar)/dz to update the density n for all charged +use the continuity equation dn/dt + d(n*upar)/dz to update the density n for all ion species """ function continuity_equation!(dens_out, fvec_in, moments, composition, dt, spectral, @@ -18,8 +18,8 @@ function continuity_equation!(dens_out, fvec_in, moments, composition, dt, spect @loop_s_r_z is ir iz begin # Use ddens_dz is upwinded using upar dens_out[iz,ir,is] -= - dt*(fvec_in.upar[iz,ir,is]*moments.charged.ddens_dz_upwind[iz,ir,is] + - fvec_in.density[iz,ir,is]*moments.charged.dupar_dz[iz,ir,is]) + dt*(fvec_in.upar[iz,ir,is]*moments.ion.ddens_dz_upwind[iz,ir,is] + + fvec_in.density[iz,ir,is]*moments.ion.dupar_dz[iz,ir,is]) end # update the density to account for ionization collisions; @@ -31,7 +31,7 @@ function continuity_equation!(dens_out, fvec_in, moments, composition, dt, spect end if ion_source_settings.active - source_amplitude = moments.charged.external_source_density_amplitude + source_amplitude = moments.ion.external_source_density_amplitude @loop_s_r_z is ir iz begin dens_out[iz,ir,is] += dt * source_amplitude[iz,ir] @@ -39,10 +39,10 @@ function continuity_equation!(dens_out, fvec_in, moments, composition, dt, spect end # Ad-hoc diffusion to stabilise numerics... - diffusion_coefficient = num_diss_params.moment_dissipation_coefficient + diffusion_coefficient = num_diss_params.ion.moment_dissipation_coefficient if diffusion_coefficient > 0.0 @loop_s_r_z is ir iz begin - dens_out[iz,ir,is] += dt*diffusion_coefficient*moments.charged.d2dens_dz2[iz,ir,is] + dens_out[iz,ir,is] += dt*diffusion_coefficient*moments.ion.d2dens_dz2[iz,ir,is] end end end @@ -80,7 +80,7 @@ function neutral_continuity_equation!(dens_out, fvec_in, moments, composition, d end # Ad-hoc diffusion to stabilise numerics... - diffusion_coefficient = num_diss_params.moment_dissipation_coefficient + diffusion_coefficient = num_diss_params.neutral.moment_dissipation_coefficient if diffusion_coefficient > 0.0 @loop_sn_r_z isn ir iz begin dens_out[iz,ir,isn] += dt*diffusion_coefficient*moments.neutral.d2dens_dz2[iz,ir,isn] diff --git a/moment_kinetics/src/coordinates.jl b/moment_kinetics/src/coordinates.jl index 13ab8abdb..95ce99da6 100644 --- a/moment_kinetics/src/coordinates.jl +++ b/moment_kinetics/src/coordinates.jl @@ -7,9 +7,10 @@ export equally_spaced_grid export set_element_boundaries using ..type_definitions: mk_float, mk_int -using ..array_allocation: allocate_float, allocate_int +using ..array_allocation: allocate_float, allocate_shared_float, allocate_int using ..calculus: derivative! using ..chebyshev: scaled_chebyshev_grid, scaled_chebyshev_radau_grid, setup_chebyshev_pseudospectral +using ..communication using ..finite_differences: finite_difference_info using ..gauss_legendre: scaled_gauss_legendre_lobatto_grid, scaled_gauss_legendre_radau_grid, setup_gausslegendre_pseudospectral using ..quadrature: composite_simpson_weights @@ -21,7 +22,7 @@ using MPI """ structure containing basic information related to coordinates """ -struct coordinate +struct coordinate{T <: AbstractVector{mk_float}} # name is the name of the variable associated with this coordiante name::String # n_global is the total number of grid points associated with this coordinate @@ -76,6 +77,12 @@ struct coordinate scratch2::Array{mk_float,1} # scratch3 is an array used for intermediate calculations requiring n entries scratch3::Array{mk_float,1} + # scratch_shared is a shared-memory array used for intermediate calculations requiring + # n entries + scratch_shared::T + # scratch_shared2 is a shared-memory array used for intermediate calculations requiring + # n entries + scratch_shared2::T # scratch_2d and scratch2_2d are arrays used for intermediate calculations requiring # ngrid x nelement entries scratch_2d::Array{mk_float,2} @@ -138,6 +145,22 @@ function define_coordinate(input, parallel_io::Bool=false; run_directory=nothing duniform_dgrid = allocate_float(input.ngrid, input.nelement_local) # scratch is an array used for intermediate calculations requiring n entries scratch = allocate_float(n_local) + if ignore_MPI + scratch_shared = allocate_float(n_local) + scratch_shared2 = allocate_float(n_local) + else + scratch_shared = allocate_shared_float(n_local) + scratch_shared2 = allocate_shared_float(n_local) + end + # Initialise scratch_shared and scratch_shared2 so that the debug checks do not + # complain when they get printed by `println(io, all_inputs)` in mk_input(). + if block_rank[] == 0 + scratch_shared .= NaN + scratch_shared2 .= NaN + end + if !ignore_MPI + _block_synchronize() + end # scratch_2d is an array used for intermediate calculations requiring ngrid x nelement entries scratch_2d = allocate_float(input.ngrid, input.nelement_local) # struct containing the advection speed options/inputs for this coordinate @@ -167,7 +190,7 @@ function define_coordinate(input, parallel_io::Bool=false; run_directory=nothing coord = coordinate(input.name, n_global, n_local, input.ngrid, input.nelement_global, input.nelement_local, input.nrank, input.irank, input.L, grid, cell_width, igrid, ielement, imin, imax, igrid_full, input.discretization, input.fd_option, input.cheb_option, - input.bc, wgts, uniform_grid, duniform_dgrid, scratch, copy(scratch), copy(scratch), + input.bc, wgts, uniform_grid, duniform_dgrid, scratch, copy(scratch), copy(scratch), scratch_shared, scratch_shared2, scratch_2d, copy(scratch_2d), advection, send_buffer, receive_buffer, input.comm, local_io_range, global_io_range, element_scale, element_shift, input.element_spacing_option, element_boundaries) diff --git a/moment_kinetics/src/derivatives.jl b/moment_kinetics/src/derivatives.jl index beeb45e66..409274b0f 100644 --- a/moment_kinetics/src/derivatives.jl +++ b/moment_kinetics/src/derivatives.jl @@ -19,7 +19,7 @@ using ..looping Centered derivatives df/dr group of rountines for fields & moments -> [z,r] -dfns (charged) -> [vpa,vperp,z,r,s] +dfns (ion) -> [vpa,vperp,z,r,s] dfns (neutrals) -> [vz,vr,vzeta,z,r,sn] """ @@ -31,6 +31,8 @@ function derivative_r!(dfdr::AbstractArray{mk_float,2}, f::AbstractArray{mk_floa r_receive_buffer1::AbstractArray{mk_float,1}, r_receive_buffer2::AbstractArray{mk_float,1}, r_spectral, r) + begin_z_region() + # differentiate f w.r.t r @loop_z iz begin @views derivative!(dfdr[iz,:], f[iz,:], r, r_spectral) @@ -87,13 +89,15 @@ function derivative_r!(dfdr::AbstractArray{mk_float,3}, f::AbstractArray{mk_floa end #df/dr -#5D version for f[vpa,vperp,z,r,s] -> charged particle dfn (species indexing taken outside this loop) +#5D version for f[vpa,vperp,z,r,s] -> ion particle dfn function derivative_r!(dfdr::AbstractArray{mk_float,5}, f::AbstractArray{mk_float,5}, dfdr_lower_endpoints::AbstractArray{mk_float,4}, dfdr_upper_endpoints::AbstractArray{mk_float,4}, r_receive_buffer1::AbstractArray{mk_float,4}, r_receive_buffer2::AbstractArray{mk_float,4}, r_spectral, r) + begin_s_z_vperp_vpa_region() + # differentiate f w.r.t r @loop_s_z_vperp_vpa is iz ivperp ivpa begin @views derivative!(dfdr[ivpa,ivperp,iz,:,is], f[ivpa,ivperp,iz,:,is], r, r_spectral) @@ -118,6 +122,8 @@ function derivative_r!(dfdr::AbstractArray{mk_float,6}, f::AbstractArray{mk_floa r_receive_buffer1::AbstractArray{mk_float,5}, r_receive_buffer2::AbstractArray{mk_float,5}, r_spectral, r) + begin_sn_z_vzeta_vr_vz_region() + # differentiate f w.r.t r @loop_sn_z_vzeta_vr_vz isn iz ivzeta ivr ivz begin @views derivative!(dfdr[ivz,ivr,ivzeta,iz,:,isn], f[ivz,ivr,ivzeta,iz,:,isn], r, r_spectral) @@ -139,7 +145,7 @@ end Centered derivatives df/dz group of rountines for fields & moments -> [z,r] -dfns (charged) -> [vpa,vperp,z,r,s] +dfns (ion) -> [vpa,vperp,z,r,s] dfns (neutrals) -> [vz,vr,vzeta,z,r,sn] """ @@ -151,6 +157,8 @@ function derivative_z!(dfdz::AbstractArray{mk_float,2}, f::AbstractArray{mk_floa z_send_buffer::AbstractArray{mk_float,1}, z_receive_buffer::AbstractArray{mk_float,1}, z_spectral, z) + begin_r_region() + # differentiate f w.r.t z @loop_r ir begin @views derivative!(dfdz[:,ir], f[:,ir], z, z_spectral) @@ -206,13 +214,15 @@ function derivative_z!(dfdz::AbstractArray{mk_float,3}, f::AbstractArray{mk_floa end end -#5D version for f[vpa,vperp,z,r,s] -> dfn charged particles +#5D version for f[vpa,vperp,z,r,s] -> dfn ions function derivative_z!(dfdz::AbstractArray{mk_float,5}, f::AbstractArray{mk_float,5}, dfdz_lower_endpoints::AbstractArray{mk_float,4}, dfdz_upper_endpoints::AbstractArray{mk_float,4}, z_send_buffer::AbstractArray{mk_float,4}, z_receive_buffer::AbstractArray{mk_float,4}, z_spectral, z) + begin_s_r_vperp_vpa_region() + # differentiate f w.r.t z @loop_s_r_vperp_vpa is ir ivperp ivpa begin @views derivative!(dfdz[ivpa,ivperp,:,ir,is], f[ivpa,ivperp,:,ir,is], z, z_spectral) @@ -237,6 +247,8 @@ function derivative_z!(dfdz::AbstractArray{mk_float,6}, f::AbstractArray{mk_floa z_send_buffer::AbstractArray{mk_float,5}, z_receive_buffer::AbstractArray{mk_float,5}, z_spectral, z) + begin_sn_r_vzeta_vr_vz_region() + # differentiate f w.r.t z @loop_sn_r_vzeta_vr_vz isn ir ivzeta ivr ivz begin @views derivative!(dfdz[ivz,ivr,ivzeta,:,ir,isn], f[ivz,ivr,ivzeta,:,ir,isn], z, z_spectral) @@ -258,7 +270,7 @@ end Upwind derivatives df/dr group of rountines for fields & moments -> [z,r] -dfns (charged) -> [vpa,vperp,z,r,s] +dfns (ion) -> [vpa,vperp,z,r,s] dfns (neutrals) -> [vz,vr,vzeta,z,r,sn] """ @@ -272,6 +284,8 @@ function derivative_r!(dfdr::AbstractArray{mk_float,2}, f::AbstractArray{mk_floa r_receive_buffer1::AbstractArray{mk_float,1}, r_receive_buffer2::AbstractArray{mk_float,1}, r_spectral, r) + begin_z_region() + # differentiate f w.r.t r @loop_z iz begin @views derivative!(dfdr[iz,:], f[iz,:], r, adv_fac[:,iz], r_spectral) @@ -335,7 +349,7 @@ function derivative_r!(dfdr::AbstractArray{mk_float,3}, f::AbstractArray{mk_floa end #df/dr -#5D version for f[vpa,vperp,z,r,s] -> charged particle dfn (species indexing taken outside this loop) +#5D version for f[vpa,vperp,z,r,s] -> ion particle dfn function derivative_r!(dfdr::AbstractArray{mk_float,5}, f::AbstractArray{mk_float,5}, advect, adv_fac_lower_buffer::AbstractArray{mk_float,4}, adv_fac_upper_buffer::AbstractArray{mk_float,4}, @@ -344,6 +358,8 @@ function derivative_r!(dfdr::AbstractArray{mk_float,5}, f::AbstractArray{mk_floa r_receive_buffer1::AbstractArray{mk_float,4}, r_receive_buffer2::AbstractArray{mk_float,4}, r_spectral, r) + begin_s_z_vperp_vpa_region() + # differentiate f w.r.t r @loop_s_z_vperp_vpa is iz ivperp ivpa begin @views derivative!(dfdr[ivpa,ivperp,iz,:,is], f[ivpa,ivperp,iz,:,is], r, advect[is].adv_fac[:,ivpa,ivperp,iz], r_spectral) @@ -373,6 +389,8 @@ function derivative_r!(dfdr::AbstractArray{mk_float,6}, f::AbstractArray{mk_floa r_receive_buffer1::AbstractArray{mk_float,5}, r_receive_buffer2::AbstractArray{mk_float,5}, r_spectral, r) + begin_sn_z_vzeta_vr_vz_region() + # differentiate f w.r.t r @loop_sn_z_vzeta_vr_vz isn iz ivzeta ivr ivz begin @views derivative!(dfdr[ivz,ivr,ivzeta,iz,:,isn], f[ivz,ivr,ivzeta,iz,:,isn], @@ -398,7 +416,7 @@ end Upwind derivatives df/dz group of rountines for fields & moments -> [z,r] -dfns (charged) -> [vpa,vperp,z,r,s] +dfns (ion) -> [vpa,vperp,z,r,s] dfns (neutrals) -> [vz,vr,vzeta,z,r,sn] """ @@ -411,6 +429,8 @@ function derivative_z!(dfdz::AbstractArray{mk_float,2}, f::AbstractArray{mk_floa z_send_buffer::AbstractArray{mk_float,1}, z_receive_buffer::AbstractArray{mk_float,1}, z_spectral, z) + begin_r_region() + # differentiate f w.r.t z @loop_r ir begin @views derivative!(dfdz[:,ir], f[:,ir], z, adv_fac[:,ir], z_spectral) @@ -472,7 +492,7 @@ function derivative_z!(dfdz::AbstractArray{mk_float,3}, f::AbstractArray{mk_floa end end -#5D version for f[vpa,vperp,z,r,s] -> dfn charged particles +#5D version for f[vpa,vperp,z,r,s] -> dfn ion particles function derivative_z!(dfdz::AbstractArray{mk_float,5}, f::AbstractArray{mk_float,5}, advect, adv_fac_lower_buffer::AbstractArray{mk_float,4}, adv_fac_upper_buffer::AbstractArray{mk_float,4}, @@ -481,6 +501,8 @@ function derivative_z!(dfdz::AbstractArray{mk_float,5}, f::AbstractArray{mk_floa z_send_buffer::AbstractArray{mk_float,4}, z_receive_buffer::AbstractArray{mk_float,4}, z_spectral, z) + begin_s_r_vperp_vpa_region() + # differentiate f w.r.t z @loop_s_r_vperp_vpa is ir ivperp ivpa begin @views derivative!(dfdz[ivpa,ivperp,:,ir,is], f[ivpa,ivperp,:,ir,is], z, advect[is].adv_fac[:,ivpa,ivperp,ir], z_spectral) @@ -510,6 +532,8 @@ function derivative_z!(dfdz::AbstractArray{mk_float,6}, f::AbstractArray{mk_floa z_send_buffer::AbstractArray{mk_float,5}, z_receive_buffer::AbstractArray{mk_float,5}, z_spectral, z) + begin_sn_r_vzeta_vr_vz_region() + # differentiate f w.r.t z @loop_sn_r_vzeta_vr_vz isn ir ivzeta ivr ivz begin @views derivative!(dfdz[ivz,ivr,ivzeta,:,ir,isn], f[ivz,ivr,ivzeta,:,ir,isn], diff --git a/moment_kinetics/src/energy_equation.jl b/moment_kinetics/src/energy_equation.jl index efafe09de..8890be3b6 100644 --- a/moment_kinetics/src/energy_equation.jl +++ b/moment_kinetics/src/energy_equation.jl @@ -17,22 +17,23 @@ function energy_equation!(ppar, fvec, moments, collisions, dt, spectral, composi begin_s_r_z_region() @loop_s_r_z is ir iz begin - ppar[iz,ir,is] += dt*(-fvec.upar[iz,ir,is]*moments.charged.dppar_dz_upwind[iz,ir,is] - - moments.charged.dqpar_dz[iz,ir,is] - - 3.0*fvec.ppar[iz,ir,is]*moments.charged.dupar_dz[iz,ir,is]) + ppar[iz,ir,is] += dt*(-fvec.upar[iz,ir,is]*moments.ion.dppar_dz_upwind[iz,ir,is] + - moments.ion.dqpar_dz[iz,ir,is] + - 3.0*fvec.ppar[iz,ir,is]*moments.ion.dupar_dz[iz,ir,is]) end + if ion_source_settings.active - source_amplitude = moments.charged.external_source_pressure_amplitude + source_amplitude = moments.ion.external_source_pressure_amplitude @loop_s_r_z is ir iz begin ppar[iz,ir,is] += dt * source_amplitude[iz,ir] end end - diffusion_coefficient = num_diss_params.moment_dissipation_coefficient + diffusion_coefficient = num_diss_params.ion.moment_dissipation_coefficient if diffusion_coefficient > 0.0 @loop_s_r_z is ir iz begin - ppar[iz,ir,is] += dt*diffusion_coefficient*moments.charged.d2ppar_dz2[iz,ir,is] + ppar[iz,ir,is] += dt*diffusion_coefficient*moments.ion.d2ppar_dz2[iz,ir,is] end end @@ -81,7 +82,7 @@ function neutral_energy_equation!(pz, fvec, moments, collisions, dt, spectral, end end - diffusion_coefficient = num_diss_params.moment_dissipation_coefficient + diffusion_coefficient = num_diss_params.neutral.moment_dissipation_coefficient if diffusion_coefficient > 0.0 @loop_sn_r_z isn ir iz begin pz[iz,ir,isn] += dt*diffusion_coefficient*moments.neutral.d2pz_dz2[iz,ir,isn] diff --git a/moment_kinetics/src/external_sources.jl b/moment_kinetics/src/external_sources.jl index 8218c610e..08b23bd5f 100644 --- a/moment_kinetics/src/external_sources.jl +++ b/moment_kinetics/src/external_sources.jl @@ -237,10 +237,10 @@ end initialize_external_source_amplitude!(moments, external_source_settings, vperp, vzeta, vr, n_neutral_species) -Initialize the arrays `moments.charged.external_source_amplitude`, -`moments.charged.external_source_density_amplitude`, -`moments.charged.external_source_momentum_amplitude`, -`moments.charged.external_source_pressure_amplitude`, +Initialize the arrays `moments.ion.external_source_amplitude`, +`moments.ion.external_source_density_amplitude`, +`moments.ion.external_source_momentum_amplitude`, +`moments.ion.external_source_pressure_amplitude`, `moments.neutral.external_source_amplitude`, `moments.neutral.external_source_density_amplitude`, `moments.neutral.external_source_momentum_amplitude`, and @@ -255,20 +255,20 @@ function initialize_external_source_amplitude!(moments, external_source_settings if ion_source_settings.active if ion_source_settings.source_type == "energy" @loop_r_z ir iz begin - moments.charged.external_source_amplitude[iz,ir] = + moments.ion.external_source_amplitude[iz,ir] = ion_source_settings.source_strength * ion_source_settings.r_amplitude[ir] * ion_source_settings.z_amplitude[iz] end if moments.evolve_density @loop_r_z ir iz begin - moments.charged.external_source_density_amplitude[iz,ir] = 0.0 + moments.ion.external_source_density_amplitude[iz,ir] = 0.0 end end if moments.evolve_upar @loop_r_z ir iz begin - moments.charged.external_source_momentum_amplitude[iz,ir] = - - moments.charged.dens[iz,ir] * moments.charged.upar[iz,ir] * + moments.ion.external_source_momentum_amplitude[iz,ir] = + - moments.ion.dens[iz,ir] * moments.ion.upar[iz,ir] * ion_source_settings.source_strength * ion_source_settings.r_amplitude[ir] * ion_source_settings.z_amplitude[iz] @@ -276,9 +276,9 @@ function initialize_external_source_amplitude!(moments, external_source_settings end if moments.evolve_ppar @loop_r_z ir iz begin - moments.charged.external_source_pressure_amplitude[iz,ir] = + moments.ion.external_source_pressure_amplitude[iz,ir] = (0.5 * ion_source_settings.source_T + - moments.charged.upar[iz,ir]^2 - moments.charged.ppar[iz,ir]) * + moments.ion.upar[iz,ir]^2 - moments.ion.ppar[iz,ir]) * ion_source_settings.source_strength * ion_source_settings.r_amplitude[ir] * ion_source_settings.z_amplitude[iz] @@ -286,14 +286,14 @@ function initialize_external_source_amplitude!(moments, external_source_settings end else @loop_r_z ir iz begin - moments.charged.external_source_amplitude[iz,ir] = + moments.ion.external_source_amplitude[iz,ir] = ion_source_settings.source_strength * ion_source_settings.r_amplitude[ir] * ion_source_settings.z_amplitude[iz] end if moments.evolve_density @loop_r_z ir iz begin - moments.charged.external_source_density_amplitude[iz,ir] = + moments.ion.external_source_density_amplitude[iz,ir] = ion_source_settings.source_strength * ion_source_settings.r_amplitude[ir] * ion_source_settings.z_amplitude[iz] @@ -301,14 +301,14 @@ function initialize_external_source_amplitude!(moments, external_source_settings end if moments.evolve_upar @loop_r_z ir iz begin - moments.charged.external_source_momentum_amplitude[iz,ir] = 0.0 + moments.ion.external_source_momentum_amplitude[iz,ir] = 0.0 end end if moments.evolve_ppar @loop_r_z ir iz begin - moments.charged.external_source_pressure_amplitude[iz,ir] = + moments.ion.external_source_pressure_amplitude[iz,ir] = (0.5 * ion_source_settings.source_T + - moments.charged.upar[iz,ir]^2) * + moments.ion.upar[iz,ir]^2) * ion_source_settings.source_strength * ion_source_settings.r_amplitude[ir] * ion_source_settings.z_amplitude[iz] @@ -393,7 +393,7 @@ end function initialize_external_source_controller_integral!( moments, external_source_settings, n_neutral_species) -Initialize the arrays `moments.charged.external_source_controller_integral` and +Initialize the arrays `moments.ion.external_source_controller_integral` and `moments.neutral.external_source_controller_integral`, using the settings in `external_source_settings` """ @@ -406,7 +406,7 @@ function initialize_external_source_controller_integral!( if ion_source_settings.PI_density_controller_I != 0.0 && ion_source_settings.source_type ∈ ("density_profile_control", "density_midpoint_control") - moments.charged.external_source_controller_integral .= 0.0 + moments.ion.external_source_controller_integral .= 0.0 end end @@ -433,7 +433,7 @@ Add external source term to the ion kinetic equation. function external_ion_source!(pdf, fvec, moments, ion_source_settings, vperp, vpa, dt, scratch_dummy) source_type = ion_source_settings.source_type - source_amplitude = moments.charged.external_source_amplitude + source_amplitude = moments.ion.external_source_amplitude source_T = ion_source_settings.source_T source_n = ion_source_settings.source_n if vperp.n == 1 @@ -446,7 +446,7 @@ function external_ion_source!(pdf, fvec, moments, ion_source_settings, vperp, vp if source_type in ("Maxwellian","energy") begin_s_r_z_vperp_region() if moments.evolve_ppar && moments.evolve_upar && moments.evolve_density - vth = moments.charged.vth + vth = moments.ion.vth density = fvec.density upar = fvec.upar @loop_s_r_z is ir iz begin @@ -744,7 +744,7 @@ function external_ion_source_controller!(fvec_in, moments, ion_source_settings, begin_r_z_region() is = 1 - ion_moments = moments.charged + ion_moments = moments.ion if ion_source_settings.source_type == "Maxwellian" if moments.evolve_ppar diff --git a/moment_kinetics/src/file_io.jl b/moment_kinetics/src/file_io.jl index 300b09079..bae07d9bc 100644 --- a/moment_kinetics/src/file_io.jl +++ b/moment_kinetics/src/file_io.jl @@ -19,7 +19,6 @@ using ..type_definitions: mk_float, mk_int using LibGit2 using MPI using Pkg -using UUIDs using TOML @debug_shared_array using ..communication: DebugMPISharedArray @@ -39,10 +38,10 @@ structure containing the various input/output streams """ struct ascii_ios{T <: Union{IOStream,Nothing}} # corresponds to the ascii file to which the distribution function is written - #ff::T + ff::T # corresponds to the ascii file to which velocity space moments of the # distribution function such as density and pressure are written - moments_charged::T + moments_ion::T moments_neutral::T # corresponds to the ascii file to which electromagnetic fields # such as the electrostatic potential are written @@ -55,7 +54,8 @@ moments & fields only """ struct io_moments_info{Tfile, Ttime, Tphi, Tmomi, Tmomn, Tchodura_lower, Tchodura_upper, Texti1, Texti2, Texti3, Texti4, - Texti5, Textn1, Textn2, Textn3, Textn4, Textn5, Tint, Tfailcause} + Texti5, Textn1, Textn2, Textn3, Textn4, Textn5, Tconstri, Tconstrn, + Tint, Tfailcause} # file identifier for the binary file to which data is written fid::Tfile # handle for the time variable @@ -66,19 +66,19 @@ struct io_moments_info{Tfile, Ttime, Tphi, Tmomi, Tmomn, Tchodura_lower, Er::Tphi # handle for the z electric field variable Ez::Tphi - # handle for the charged species density + # handle for the ion species density density::Tmomi - # handle for the charged species parallel flow + # handle for the ion species parallel flow parallel_flow::Tmomi - # handle for the charged species parallel pressure + # handle for the ion species parallel pressure parallel_pressure::Tmomi - # handle for the charged species perpendicular pressure + # handle for the ion species perpendicular pressure perpendicular_pressure::Tmomi - # handle for the charged species parallel heat flux + # handle for the ion species parallel heat flux parallel_heat_flux::Tmomi - # handle for the charged species thermal speed + # handle for the ion species thermal speed thermal_speed::Tmomi - # handle for the charged species entropy production + # handle for the ion species entropy production entropy_production::Tmomi # handle for chodura diagnostic (lower) chodura_integral_lower::Tchodura_lower @@ -103,6 +103,14 @@ struct io_moments_info{Tfile, Ttime, Tphi, Tmomi, Tmomn, Tchodura_lower, external_source_neutral_pressure_amplitude::Textn4 external_source_neutral_controller_integral::Textn5 + # handles for constraint coefficients + ion_constraints_A_coefficient::Tconstri + ion_constraints_B_coefficient::Tconstri + ion_constraints_C_coefficient::Tconstri + neutral_constraints_A_coefficient::Tconstrn + neutral_constraints_B_coefficient::Tconstrn + neutral_constraints_C_coefficient::Tconstrn + # cumulative wall clock time taken by the run time_for_run::Ttime # cumulative number of timesteps taken @@ -130,7 +138,7 @@ distribution function data only struct io_dfns_info{Tfile, Tfi, Tfn, Tmoments} # file identifier for the binary file to which data is written fid::Tfile - # handle for the charged species distribution function variable + # handle for the ion species distribution function variable f::Tfi # handle for the neutral species distribution function variable f_neutral::Tfn @@ -197,16 +205,16 @@ function setup_file_io(io_input, boundary_distributions, vz, vr, vzeta, vpa, vpe out_prefix = joinpath(io_input.output_dir, io_input.run_name) if io_input.ascii_output - #ff_io = open_ascii_output_file(out_prefix, "f_vs_t") - mom_chrg_io = open_ascii_output_file(out_prefix, "moments_charged_vs_t") + ff_io = open_ascii_output_file(out_prefix, "f_vs_t") + mom_ion_io = open_ascii_output_file(out_prefix, "moments_ion_vs_t") mom_ntrl_io = open_ascii_output_file(out_prefix, "moments_neutral_vs_t") fields_io = open_ascii_output_file(out_prefix, "fields_vs_t") - ascii = ascii_ios(mom_chrg_io, mom_ntrl_io, fields_io) + ascii = ascii_ios(ff_io, mom_ion_io, mom_ntrl_io, fields_io) else - ascii = ascii_ios(nothing, nothing, nothing) + ascii = ascii_ios(nothing, nothing, nothing, nothing) end - run_id = string(uuid4()) + run_id = io_input.run_id io_moments = setup_moments_io(out_prefix, io_input.binary_format, vz, vr, vzeta, vpa, vperp, r, z, composition, collisions, @@ -306,14 +314,6 @@ Write provenance tracking information, to allow runs to be reproduced. function write_provenance_tracking_info!(fid, parallel_io, run_id, restart_time_index, input_dict, previous_runs_info) - if !parallel_io - # Communicate run_id to all blocks - # Need to convert run_id to a Vector{Char} for MPI - run_id_chars = [run_id...] - MPI.Bcast!(run_id_chars, 0, comm_inter_block[]) - run_id = string(run_id_chars...) - end - @serial_region begin provenance_tracking = create_io_group(fid, "provenance_tracking") @@ -426,14 +426,14 @@ function write_boundary_distributions!(fid, boundary_distributions, parallel_io, @serial_region begin boundary_distributions_io = create_io_group(fid, "boundary_distributions") - write_single_value!(boundary_distributions_io, "pdf_rboundary_charged_left", - boundary_distributions.pdf_rboundary_charged[:,:,:,1,:], vpa, vperp, z, + write_single_value!(boundary_distributions_io, "pdf_rboundary_ion_left", + boundary_distributions.pdf_rboundary_ion[:,:,:,1,:], vpa, vperp, z, parallel_io=parallel_io, n_ion_species=composition.n_ion_species, - description="Initial charged-particle pdf at left radial boundary") - write_single_value!(boundary_distributions_io, "pdf_rboundary_charged_right", - boundary_distributions.pdf_rboundary_charged[:,:,:,2,:], vpa, vperp, z, + description="Initial ion-particle pdf at left radial boundary") + write_single_value!(boundary_distributions_io, "pdf_rboundary_ion_right", + boundary_distributions.pdf_rboundary_ion[:,:,:,2,:], vpa, vperp, z, parallel_io=parallel_io, n_ion_species=composition.n_ion_species, - description="Initial charged-particle pdf at right radial boundary") + description="Initial ion-particle pdf at right radial boundary") write_single_value!(boundary_distributions_io, "pdf_rboundary_neutral_left", boundary_distributions.pdf_rboundary_neutral[:,:,:,:,1,:], vz, vr, vzeta, z, parallel_io=parallel_io, n_neutral_species=composition.n_neutral_species, @@ -454,12 +454,16 @@ function define_io_coordinates!(fid, vz, vr, vzeta, vpa, vperp, z, r, parallel_i @serial_region begin # create the "coords" group that will contain coordinate information coords = create_io_group(fid, "coords") - # create the "z" sub-group of "coords" that will contain z coordinate info, - # including total number of grid points and grid point locations - define_io_coordinate!(coords, z, "z", "spatial coordinate z", parallel_io) - # create the "r" sub-group of "coords" that will contain r coordinate info, - # including total number of grid points and grid point locations - define_io_coordinate!(coords, r, "r", "spatial coordinate r", parallel_io) + if z !== nothing + # create the "z" sub-group of "coords" that will contain z coordinate info, + # including total number of grid points and grid point locations + define_io_coordinate!(coords, z, "z", "spatial coordinate z", parallel_io) + end + if r !== nothing + # create the "r" sub-group of "coords" that will contain r coordinate info, + # including total number of grid points and grid point locations + define_io_coordinate!(coords, r, "r", "spatial coordinate r", parallel_io) + end if parallel_io # Parallel I/O produces a single file, so effectively a 'single block' @@ -487,24 +491,34 @@ function define_io_coordinates!(fid, vz, vr, vzeta, vpa, vperp, z, r, parallel_i parallel_io=parallel_io, description="number of zr blocks") end - # create the "vz" sub-group of "coords" that will contain vz coordinate info, - # including total number of grid points and grid point locations - define_io_coordinate!(coords, vz, "vz", "velocity coordinate v_z", parallel_io) - # create the "vr" sub-group of "coords" that will contain vr coordinate info, - # including total number of grid points and grid point locations - define_io_coordinate!(coords, vr, "vr", "velocity coordinate v_r", parallel_io) - # create the "vzeta" sub-group of "coords" that will contain vzeta coordinate info, - # including total number of grid points and grid point locations - define_io_coordinate!(coords, vzeta, "vzeta", "velocity coordinate v_zeta", - parallel_io) - # create the "vpa" sub-group of "coords" that will contain vpa coordinate info, - # including total number of grid points and grid point locations - define_io_coordinate!(coords, vpa, "vpa", "velocity coordinate v_parallel", - parallel_io) - # create the "vperp" sub-group of "coords" that will contain vperp coordinate info, - # including total number of grid points and grid point locations - define_io_coordinate!(coords, vperp, "vperp", "velocity coordinate v_perp", - parallel_io) + if vz !== nothing + # create the "vz" sub-group of "coords" that will contain vz coordinate info, + # including total number of grid points and grid point locations + define_io_coordinate!(coords, vz, "vz", "velocity coordinate v_z", parallel_io) + end + if vr !== nothing + # create the "vr" sub-group of "coords" that will contain vr coordinate info, + # including total number of grid points and grid point locations + define_io_coordinate!(coords, vr, "vr", "velocity coordinate v_r", parallel_io) + end + if vzeta !== nothing + # create the "vzeta" sub-group of "coords" that will contain vzeta coordinate info, + # including total number of grid points and grid point locations + define_io_coordinate!(coords, vzeta, "vzeta", "velocity coordinate v_zeta", + parallel_io) + end + if vpa !== nothing + # create the "vpa" sub-group of "coords" that will contain vpa coordinate info, + # including total number of grid points and grid point locations + define_io_coordinate!(coords, vpa, "vpa", "velocity coordinate v_parallel", + parallel_io) + end + if vperp !== nothing + # create the "vperp" sub-group of "coords" that will contain vperp coordinate info, + # including total number of grid points and grid point locations + define_io_coordinate!(coords, vperp, "vperp", "velocity coordinate v_perp", + parallel_io) + end end return nothing @@ -637,233 +651,31 @@ function define_dynamic_moment_variables!(fid, n_ion_species, n_neutral_species, io_time = create_dynamic_variable!(dynamic, "time", mk_float; parallel_io=parallel_io, description="simulation time") - # io_phi is the handle referring to the electrostatic potential phi - io_phi = create_dynamic_variable!(dynamic, "phi", mk_float, z, r; - parallel_io=parallel_io, - description="electrostatic potential", - units="T_ref/e") - # io_Er is the handle for the radial component of the electric field - io_Er = create_dynamic_variable!(dynamic, "Er", mk_float, z, r; - parallel_io=parallel_io, - description="radial electric field", - units="T_ref/e L_ref") - # io_Ez is the handle for the zed component of the electric field - io_Ez = create_dynamic_variable!(dynamic, "Ez", mk_float, z, r; - parallel_io=parallel_io, - description="vertical electric field", - units="T_ref/e L_ref") - - # io_density is the handle for the ion particle density - io_density = create_dynamic_variable!(dynamic, "density", mk_float, z, r; - n_ion_species=n_ion_species, - parallel_io=parallel_io, - description="charged species density", - units="n_ref") - - # io_upar is the handle for the ion parallel flow density - io_upar = create_dynamic_variable!(dynamic, "parallel_flow", mk_float, z, r; - n_ion_species=n_ion_species, - parallel_io=parallel_io, - description="charged species parallel flow", - units="c_ref = sqrt(2*T_ref/mi)") - - # io_ppar is the handle for the ion parallel pressure - io_ppar = create_dynamic_variable!(dynamic, "parallel_pressure", mk_float, z, r; - n_ion_species=n_ion_species, - parallel_io=parallel_io, - description="charged species parallel pressure", - units="n_ref*T_ref") - - # io_pperp is the handle for the ion parallel pressure - io_pperp = create_dynamic_variable!(dynamic, "perpendicular_pressure", mk_float, z, r; - n_ion_species=n_ion_species, - parallel_io=parallel_io, - description="charged species perpendicular pressure", - units="n_ref*T_ref") - - # io_qpar is the handle for the ion parallel heat flux - io_qpar = create_dynamic_variable!(dynamic, "parallel_heat_flux", mk_float, z, r; - n_ion_species=n_ion_species, - parallel_io=parallel_io, - description="charged species parallel heat flux", - units="n_ref*T_ref*c_ref") - - # io_vth is the handle for the ion thermal speed - io_vth = create_dynamic_variable!(dynamic, "thermal_speed", mk_float, z, r; - n_ion_species=n_ion_species, - parallel_io=parallel_io, - description="charged species thermal speed", - units="c_ref") - - # io_dSdt is the handle for the entropy production (due to collisions) - io_dSdt = create_dynamic_variable!(dynamic, "entropy_production", mk_float, z, r; - n_ion_species=n_ion_species, - parallel_io=parallel_io, - description="charged species entropy production", - units="") - - if parallel_io || z.irank == 0 - # io_chodura_lower is the handle for the ion thermal speed - io_chodura_lower = create_dynamic_variable!(dynamic, "chodura_integral_lower", mk_float, r; - n_ion_species=n_ion_species, - parallel_io=parallel_io, - description="Generalised Chodura integral lower sheath entrance", - units="c_ref") - else - io_chodura_lower = nothing - end - if parallel_io || z.irank == z.nrank - 1 - # io_chodura_upper is the handle for the ion thermal speed - io_chodura_upper = create_dynamic_variable!(dynamic, "chodura_integral_upper", mk_float, r; - n_ion_species=n_ion_species, - parallel_io=parallel_io, - description="Generalised Chodura integral upper sheath entrance", - units="c_ref") - else - io_chodura_upper = nothing - end - # io_density_neutral is the handle for the neutral particle density - io_density_neutral = create_dynamic_variable!(dynamic, "density_neutral", mk_float, z, r; - n_neutral_species=n_neutral_species, - parallel_io=parallel_io, - description="neutral species density", - units="n_ref") - - # io_uz_neutral is the handle for the neutral z momentum density - io_uz_neutral = create_dynamic_variable!(dynamic, "uz_neutral", mk_float, z, r; - n_neutral_species=n_neutral_species, - parallel_io=parallel_io, - description="neutral species mean z velocity", - units="c_ref = sqrt(2*T_ref/mi)") - - # io_pz_neutral is the handle for the neutral species zz pressure - io_pz_neutral = create_dynamic_variable!(dynamic, "pz_neutral", mk_float, z, r; - n_neutral_species=n_neutral_species, - parallel_io=parallel_io, - description="neutral species mean zz pressure", - units="n_ref*T_ref") - - # io_qz_neutral is the handle for the neutral z heat flux - io_qz_neutral = create_dynamic_variable!(dynamic, "qz_neutral", mk_float, z, r; - n_neutral_species=n_neutral_species, - parallel_io=parallel_io, - description="neutral species z heat flux", - units="n_ref*T_ref*c_ref") - - # io_thermal_speed_neutral is the handle for the neutral thermal speed - io_thermal_speed_neutral = create_dynamic_variable!( - dynamic, "thermal_speed_neutral", mk_float, z, r; - n_neutral_species=n_neutral_species, - parallel_io=parallel_io, description="neutral species thermal speed", - units="c_ref") - - ion_source_settings = external_source_settings.ion - if ion_source_settings.active - external_source_amplitude = create_dynamic_variable!( - dynamic, "external_source_amplitude", mk_float, z, r; - parallel_io=parallel_io, description="Amplitude of the external source for ions", - units="n_ref/c_ref^3*c_ref/L_ref") - if evolve_density - external_source_density_amplitude = create_dynamic_variable!( - dynamic, "external_source_density_amplitude", mk_float, z, r; - parallel_io=parallel_io, description="Amplitude of the external density source for ions", - units="n_ref*c_ref/L_ref") - else - external_source_density_amplitude = nothing - end - if evolve_upar - external_source_momentum_amplitude = create_dynamic_variable!( - dynamic, "external_source_momentum_amplitude", mk_float, z, r; - parallel_io=parallel_io, description="Amplitude of the external momentum source for ions", - units="m_ref*n_ref*c_ref*c_ref/L_ref") - else - external_source_momentum_amplitude = nothing - end - if evolve_ppar - external_source_pressure_amplitude = create_dynamic_variable!( - dynamic, "external_source_pressure_amplitude", mk_float, z, r; - parallel_io=parallel_io, description="Amplitude of the external pressure source for ions", - units="m_ref*n_ref*c_ref^2*c_ref/L_ref") - else - external_source_pressure_amplitude = nothing - end - if ion_source_settings.PI_density_controller_I != 0.0 && - ion_source_settings.source_type ∈ ("density_profile_control", "density_midpoint_control") - if ion_source_settings.source_type == "density_profile_control" - external_source_controller_integral = create_dynamic_variable!( - dynamic, "external_source_controller_integral", mk_float, z, r; - parallel_io=parallel_io, - description="Integral term for the PID controller of the external source for ions") - else - external_source_controller_integral = create_dynamic_variable!( - dynamic, "external_source_controller_integral", mk_float; - parallel_io=parallel_io, - description="Integral term for the PID controller of the external source for ions") - end - else - external_source_controller_integral = nothing - end - else - external_source_amplitude = nothing - external_source_density_amplitude = nothing - external_source_momentum_amplitude = nothing - external_source_pressure_amplitude = nothing - external_source_controller_integral = nothing - end - - neutral_source_settings = external_source_settings.neutral - if n_neutral_species > 0 && neutral_source_settings.active - external_source_neutral_amplitude = create_dynamic_variable!( - dynamic, "external_source_neutral_amplitude", mk_float, z, r; - parallel_io=parallel_io, description="Amplitude of the external source for neutrals", - units="n_ref/c_ref^3*c_ref/L_ref") - if evolve_density - external_source_neutral_density_amplitude = create_dynamic_variable!( - dynamic, "external_source_neutral_density_amplitude", mk_float, z, r; - parallel_io=parallel_io, description="Amplitude of the external density source for neutrals", - units="n_ref*c_ref/L_ref") - else - external_source_neutral_density_amplitude = nothing - end - if evolve_upar - external_source_neutral_momentum_amplitude = create_dynamic_variable!( - dynamic, "external_source_neutral_momentum_amplitude", mk_float, z, r; - parallel_io=parallel_io, description="Amplitude of the external momentum source for neutrals", - units="m_ref*n_ref*c_ref*c_ref/L_ref") - else - external_source_neutral_momentum_amplitude = nothing - end - if evolve_ppar - external_source_neutral_pressure_amplitude = create_dynamic_variable!( - dynamic, "external_source_neutral_pressure_amplitude", mk_float, z, r; - parallel_io=parallel_io, description="Amplitude of the external pressure source for neutrals", - units="m_ref*n_ref*c_ref^2*c_ref/L_ref") - else - external_source_neutral_pressure_amplitude = nothing - end - if neutral_source_settings.PI_density_controller_I != 0.0 && - neutral_source_settings.source_type ∈ ("density_profile_control", "density_midpoint_control") - if neutral_source_settings.source_type == "density_profile_control" - external_source_neutral_controller_integral = create_dynamic_variable!( - dynamic, "external_source_neutral_controller_integral", mk_float, z, r; - parallel_io=parallel_io, - description="Integral term for the PID controller of the external source for neutrals") - else - external_source_neutral_controller_integral = create_dynamic_variable!( - dynamic, "external_source_neutral_controller_integral", mk_float; - parallel_io=parallel_io, - description="Integral term for the PID controller of the external source for neutrals") - end - else - external_source_neutral_controller_integral = nothing - end - else - external_source_neutral_amplitude = nothing - external_source_neutral_density_amplitude = nothing - external_source_neutral_momentum_amplitude = nothing - external_source_neutral_pressure_amplitude = nothing - external_source_neutral_controller_integral = nothing - end + io_phi, io_Er, io_Ez = + define_dynamic_em_field_variables!(fid, r, z, parallel_io) + + io_density, io_upar, io_ppar, io_pperp, io_qpar, io_vth, io_dSdt, + external_source_amplitude, external_source_density_amplitude, + external_source_momentum_amplitude, external_source_pressure_amplitude, + external_source_controller_integral, io_chodura_lower, io_chodura_upper, + ion_constraints_A_coefficient, ion_constraints_B_coefficient, + ion_constraints_C_coefficient = + define_dynamic_ion_moment_variables!(fid, n_ion_species, r, z, parallel_io, + external_source_settings, evolve_density, + evolve_upar, evolve_ppar) + + io_density_neutral, io_uz_neutral, io_pz_neutral, io_qz_neutral, + io_thermal_speed_neutral, external_source_neutral_amplitude, + external_source_neutral_density_amplitude, + external_source_neutral_momentum_amplitude, + external_source_neutral_pressure_amplitude, + external_source_neutral_controller_integral, neutral_constraints_A_coefficient, + neutral_constraints_B_coefficient, neutral_constraints_C_coefficient = + define_dynamic_neutral_moment_variables!(fid, n_neutral_species, r, z, + parallel_io, + external_source_settings, + evolve_density, evolve_upar, + evolve_ppar) io_time_for_run = create_dynamic_variable!( dynamic, "time_for_run", mk_float; parallel_io=parallel_io, @@ -907,7 +719,8 @@ function define_dynamic_moment_variables!(fid, n_ion_species, n_neutral_species, * "used by adaptve timestepping algorithm") return io_moments_info(fid, io_time, io_phi, io_Er, io_Ez, io_density, io_upar, - io_ppar, io_pperp, io_qpar, io_vth, io_dSdt, io_chodura_lower, io_chodura_upper, io_density_neutral, io_uz_neutral, + io_ppar, io_pperp, io_qpar, io_vth, io_dSdt, io_chodura_lower, io_chodura_upper, + io_density_neutral, io_uz_neutral, io_pz_neutral, io_qz_neutral, io_thermal_speed_neutral, external_source_amplitude, external_source_density_amplitude, @@ -919,6 +732,12 @@ function define_dynamic_moment_variables!(fid, n_ion_species, n_neutral_species, external_source_neutral_momentum_amplitude, external_source_neutral_pressure_amplitude, external_source_neutral_controller_integral, + ion_constraints_A_coefficient, + ion_constraints_B_coefficient, + ion_constraints_C_coefficient, + neutral_constraints_A_coefficient, + neutral_constraints_B_coefficient, + neutral_constraints_C_coefficient, io_time_for_run, io_step_counter, io_dt, io_failure_counter, io_failure_caused_by, io_limit_caused_by, io_dt_before_last_fail, parallel_io) @@ -928,18 +747,337 @@ function define_dynamic_moment_variables!(fid, n_ion_species, n_neutral_species, return nothing end +""" +define dynamic (time-evolving) electromagnetic field variables for writing to the hdf5 +file +""" +function define_dynamic_em_field_variables!(fid, r::coordinate, z::coordinate, + parallel_io) + + dynamic = get_group(fid, "dynamic_data") + + # io_phi is the handle referring to the electrostatic potential phi + io_phi = create_dynamic_variable!(dynamic, "phi", mk_float, z, r; + parallel_io=parallel_io, + description="electrostatic potential", + units="T_ref/e") + # io_Er is the handle for the radial component of the electric field + io_Er = create_dynamic_variable!(dynamic, "Er", mk_float, z, r; + parallel_io=parallel_io, + description="radial electric field", + units="T_ref/e L_ref") + # io_Ez is the handle for the zed component of the electric field + io_Ez = create_dynamic_variable!(dynamic, "Ez", mk_float, z, r; + parallel_io=parallel_io, + description="vertical electric field", + units="T_ref/e L_ref") + + return io_phi, io_Er, io_Ez +end + +""" +define dynamic (time-evolving) ion moment variables for writing to the hdf5 file +""" +function define_dynamic_ion_moment_variables!(fid, n_ion_species, r::coordinate, + z::coordinate, parallel_io, external_source_settings, evolve_density, evolve_upar, + evolve_ppar) + + dynamic = get_group(fid, "dynamic_data") + + # io_density is the handle for the ion particle density + io_density = create_dynamic_variable!(dynamic, "density", mk_float, z, r; + n_ion_species=n_ion_species, + parallel_io=parallel_io, + description="ion species density", + units="n_ref") + + # io_upar is the handle for the ion parallel flow density + io_upar = create_dynamic_variable!(dynamic, "parallel_flow", mk_float, z, r; + n_ion_species=n_ion_species, + parallel_io=parallel_io, + description="ion species parallel flow", + units="c_ref = sqrt(2*T_ref/mi)") + + # io_ppar is the handle for the ion parallel pressure + io_ppar = create_dynamic_variable!(dynamic, "parallel_pressure", mk_float, z, r; + n_ion_species=n_ion_species, + parallel_io=parallel_io, + description="ion species parallel pressure", + units="n_ref*T_ref") + + # io_pperp is the handle for the ion parallel pressure + io_pperp = create_dynamic_variable!(dynamic, "perpendicular_pressure", mk_float, z, r; + n_ion_species=n_ion_species, + parallel_io=parallel_io, + description="ion species perpendicular pressure", + units="n_ref*T_ref") + + # io_qpar is the handle for the ion parallel heat flux + io_qpar = create_dynamic_variable!(dynamic, "parallel_heat_flux", mk_float, z, r; + n_ion_species=n_ion_species, + parallel_io=parallel_io, + description="ion species parallel heat flux", + units="n_ref*T_ref*c_ref") + + # io_vth is the handle for the ion thermal speed + io_vth = create_dynamic_variable!(dynamic, "thermal_speed", mk_float, z, r; + n_ion_species=n_ion_species, + parallel_io=parallel_io, + description="ion species thermal speed", + units="c_ref") + + # io_dSdt is the handle for the entropy production (due to collisions) + io_dSdt = create_dynamic_variable!(dynamic, "entropy_production", mk_float, z, r; + n_ion_species=n_ion_species, + parallel_io=parallel_io, + description="ion species entropy production", + units="") + + ion_source_settings = external_source_settings.ion + if ion_source_settings.active + external_source_amplitude = create_dynamic_variable!( + dynamic, "external_source_amplitude", mk_float, z, r; + parallel_io=parallel_io, description="Amplitude of the external source for ions", + units="n_ref/c_ref^3*c_ref/L_ref") + if evolve_density + external_source_density_amplitude = create_dynamic_variable!( + dynamic, "external_source_density_amplitude", mk_float, z, r; + parallel_io=parallel_io, description="Amplitude of the external density source for ions", + units="n_ref*c_ref/L_ref") + else + external_source_density_amplitude = nothing + end + if evolve_upar + external_source_momentum_amplitude = create_dynamic_variable!( + dynamic, "external_source_momentum_amplitude", mk_float, z, r; + parallel_io=parallel_io, description="Amplitude of the external momentum source for ions", + units="m_ref*n_ref*c_ref*c_ref/L_ref") + else + external_source_momentum_amplitude = nothing + end + if evolve_ppar + external_source_pressure_amplitude = create_dynamic_variable!( + dynamic, "external_source_pressure_amplitude", mk_float, z, r; + parallel_io=parallel_io, description="Amplitude of the external pressure source for ions", + units="m_ref*n_ref*c_ref^2*c_ref/L_ref") + else + external_source_pressure_amplitude = nothing + end + if ion_source_settings.PI_density_controller_I != 0.0 && + ion_source_settings.source_type ∈ ("density_profile_control", "density_midpoint_control") + if ion_source_settings.source_type == "density_profile_control" + external_source_controller_integral = create_dynamic_variable!( + dynamic, "external_source_controller_integral", mk_float, z, r; + parallel_io=parallel_io, + description="Integral term for the PID controller of the external source for ions") + else + external_source_controller_integral = create_dynamic_variable!( + dynamic, "external_source_controller_integral", mk_float; + parallel_io=parallel_io, + description="Integral term for the PID controller of the external source for ions") + end + else + external_source_controller_integral = nothing + end + else + external_source_amplitude = nothing + external_source_density_amplitude = nothing + external_source_momentum_amplitude = nothing + external_source_pressure_amplitude = nothing + external_source_controller_integral = nothing + end + + if parallel_io || z.irank == 0 + # io_chodura_lower is the handle for the ion thermal speed + io_chodura_lower = create_dynamic_variable!(dynamic, "chodura_integral_lower", mk_float, r; + n_ion_species=n_ion_species, + parallel_io=parallel_io, + description="Generalised Chodura integral lower sheath entrance", + units="c_ref") + else + io_chodura_lower = nothing + end + if parallel_io || z.irank == z.nrank - 1 + # io_chodura_upper is the handle for the ion thermal speed + io_chodura_upper = create_dynamic_variable!(dynamic, "chodura_integral_upper", mk_float, r; + n_ion_species=n_ion_species, + parallel_io=parallel_io, + description="Generalised Chodura integral upper sheath entrance", + units="c_ref") + else + io_chodura_upper = nothing + end + + if evolve_density || evolve_upar || evolve_ppar + ion_constraints_A_coefficient = + create_dynamic_variable!(dynamic, "ion_constraints_A_coefficient", mk_float, z, r; + n_ion_species=n_ion_species, + parallel_io=parallel_io, + description="'A' coefficient enforcing density constraint for ions") + ion_constraints_B_coefficient = + create_dynamic_variable!(dynamic, "ion_constraints_B_coefficient", mk_float, z, r; + n_ion_species=n_ion_species, + parallel_io=parallel_io, + description="'B' coefficient enforcing flow constraint for ions") + ion_constraints_C_coefficient = + create_dynamic_variable!(dynamic, "ion_constraints_C_coefficient", mk_float, z, r; + n_ion_species=n_ion_species, + parallel_io=parallel_io, + description="'C' coefficient enforcing pressure constraint for ions") + else + ion_constraints_A_coefficient = nothing + ion_constraints_B_coefficient = nothing + ion_constraints_C_coefficient = nothing + end + + return io_density, io_upar, io_ppar, io_pperp, io_qpar, io_vth, io_dSdt, + external_source_amplitude, external_source_density_amplitude, + external_source_momentum_amplitude, external_source_pressure_amplitude, + external_source_controller_integral, io_chodura_lower, io_chodura_upper, + ion_constraints_A_coefficient, ion_constraints_B_coefficient, + ion_constraints_C_coefficient +end + +""" +define dynamic (time-evolving) neutral moment variables for writing to the hdf5 file +""" +function define_dynamic_neutral_moment_variables!(fid, n_neutral_species, r::coordinate, + z::coordinate, parallel_io, external_source_settings, evolve_density, evolve_upar, + evolve_ppar) + + dynamic = get_group(fid, "dynamic_data") + + # io_density_neutral is the handle for the neutral particle density + io_density_neutral = create_dynamic_variable!(dynamic, "density_neutral", mk_float, z, r; + n_neutral_species=n_neutral_species, + parallel_io=parallel_io, + description="neutral species density", + units="n_ref") + + # io_uz_neutral is the handle for the neutral z momentum density + io_uz_neutral = create_dynamic_variable!(dynamic, "uz_neutral", mk_float, z, r; + n_neutral_species=n_neutral_species, + parallel_io=parallel_io, + description="neutral species mean z velocity", + units="c_ref = sqrt(2*T_ref/mi)") + + # io_pz_neutral is the handle for the neutral species zz pressure + io_pz_neutral = create_dynamic_variable!(dynamic, "pz_neutral", mk_float, z, r; + n_neutral_species=n_neutral_species, + parallel_io=parallel_io, + description="neutral species mean zz pressure", + units="n_ref*T_ref") + + # io_qz_neutral is the handle for the neutral z heat flux + io_qz_neutral = create_dynamic_variable!(dynamic, "qz_neutral", mk_float, z, r; + n_neutral_species=n_neutral_species, + parallel_io=parallel_io, + description="neutral species z heat flux", + units="n_ref*T_ref*c_ref") + + # io_thermal_speed_neutral is the handle for the neutral thermal speed + io_thermal_speed_neutral = create_dynamic_variable!( + dynamic, "thermal_speed_neutral", mk_float, z, r; + n_neutral_species=n_neutral_species, + parallel_io=parallel_io, description="neutral species thermal speed", + units="c_ref") + + neutral_source_settings = external_source_settings.neutral + if n_neutral_species > 0 && neutral_source_settings.active + external_source_neutral_amplitude = create_dynamic_variable!( + dynamic, "external_source_neutral_amplitude", mk_float, z, r; + parallel_io=parallel_io, description="Amplitude of the external source for neutrals", + units="n_ref/c_ref^3*c_ref/L_ref") + if evolve_density + external_source_neutral_density_amplitude = create_dynamic_variable!( + dynamic, "external_source_neutral_density_amplitude", mk_float, z, r; + parallel_io=parallel_io, description="Amplitude of the external density source for neutrals", + units="n_ref*c_ref/L_ref") + else + external_source_neutral_density_amplitude = nothing + end + if evolve_upar + external_source_neutral_momentum_amplitude = create_dynamic_variable!( + dynamic, "external_source_neutral_momentum_amplitude", mk_float, z, r; + parallel_io=parallel_io, description="Amplitude of the external momentum source for neutrals", + units="m_ref*n_ref*c_ref*c_ref/L_ref") + else + external_source_neutral_momentum_amplitude = nothing + end + if evolve_ppar + external_source_neutral_pressure_amplitude = create_dynamic_variable!( + dynamic, "external_source_neutral_pressure_amplitude", mk_float, z, r; + parallel_io=parallel_io, description="Amplitude of the external pressure source for neutrals", + units="m_ref*n_ref*c_ref^2*c_ref/L_ref") + else + external_source_neutral_pressure_amplitude = nothing + end + if neutral_source_settings.PI_density_controller_I != 0.0 && + neutral_source_settings.source_type ∈ ("density_profile_control", "density_midpoint_control") + if neutral_source_settings.source_type == "density_profile_control" + external_source_neutral_controller_integral = create_dynamic_variable!( + dynamic, "external_source_neutral_controller_integral", mk_float, z, r; + parallel_io=parallel_io, + description="Integral term for the PID controller of the external source for neutrals") + else + external_source_neutral_controller_integral = create_dynamic_variable!( + dynamic, "external_source_neutral_controller_integral", mk_float; + parallel_io=parallel_io, + description="Integral term for the PID controller of the external source for neutrals") + end + else + external_source_neutral_controller_integral = nothing + end + else + external_source_neutral_amplitude = nothing + external_source_neutral_density_amplitude = nothing + external_source_neutral_momentum_amplitude = nothing + external_source_neutral_pressure_amplitude = nothing + external_source_neutral_controller_integral = nothing + end + + if evolve_density || evolve_upar || evolve_ppar + neutral_constraints_A_coefficient = + create_dynamic_variable!(dynamic, "neutral_constraints_A_coefficient", mk_float, z, r; + n_neutral_species=n_neutral_species, + parallel_io=parallel_io, + description="'A' coefficient enforcing density constraint for neutrals") + neutral_constraints_B_coefficient = + create_dynamic_variable!(dynamic, "neutral_constraints_B_coefficient", mk_float, z, r; + n_neutral_species=n_neutral_species, + parallel_io=parallel_io, + description="'B' coefficient enforcing flow constraint for neutrals") + neutral_constraints_C_coefficient = + create_dynamic_variable!(dynamic, "neutral_constraints_C_coefficient", mk_float, z, r; + n_neutral_species=n_neutral_species, + parallel_io=parallel_io, + description="'C' coefficient enforcing pressure constraint for neutrals") + else + neutral_constraints_A_coefficient = nothing + neutral_constraints_B_coefficient = nothing + neutral_constraints_C_coefficient = nothing + end + + return io_density_neutral, io_uz_neutral, io_pz_neutral, io_qz_neutral, + io_thermal_speed_neutral, external_source_neutral_amplitude, + external_source_neutral_density_amplitude, + external_source_neutral_momentum_amplitude, + external_source_neutral_pressure_amplitude, + external_source_neutral_controller_integral, neutral_constraints_A_coefficient, + neutral_constraints_B_coefficient, neutral_constraints_C_coefficient +end + """ define dynamic (time-evolving) distribution function variables for writing to the output file """ -function define_dynamic_dfn_variables!(fid, r, z, vperp, vpa, vzeta, vr, vz, - n_ion_species, n_neutral_species, parallel_io, - external_source_settings, evolve_density, - evolve_upar, evolve_ppar) +function define_dynamic_dfn_variables!(fid, r, z, vperp, vpa, vzeta, vr, vz, composition, + parallel_io, external_source_settings, + evolve_density, evolve_upar, evolve_ppar) @serial_region begin - io_moments = define_dynamic_moment_variables!(fid, n_ion_species, - n_neutral_species, r, z, + io_moments = define_dynamic_moment_variables!(fid, composition.n_ion_species, + composition.n_neutral_species, r, z, parallel_io, external_source_settings, evolve_density, evolve_upar, @@ -949,13 +1087,13 @@ function define_dynamic_dfn_variables!(fid, r, z, vperp, vpa, vzeta, vr, vz, # io_f is the handle for the ion pdf io_f = create_dynamic_variable!(dynamic, "f", mk_float, vpa, vperp, z, r; - n_ion_species=n_ion_species, + n_ion_species=composition.n_ion_species, parallel_io=parallel_io, - description="charged species distribution function") + description="ion species distribution function") # io_f_neutral is the handle for the neutral pdf io_f_neutral = create_dynamic_variable!(dynamic, "f_neutral", mk_float, vz, vr, vzeta, z, r; - n_neutral_species=n_neutral_species, + n_neutral_species=composition.n_neutral_species, parallel_io=parallel_io, description="neutral species distribution function") @@ -1091,6 +1229,12 @@ function reopen_moments_io(file_info) getvar("external_source_neutral_momentum_amplitude"), getvar("external_source_neutral_pressure_amplitude"), getvar("external_source_neutral_controller_integral"), + getvar("ion_constraints_A_coefficient"), + getvar("ion_constraints_B_coefficient"), + getvar("ion_constraints_C_coefficient"), + getvar("neutral_constraints_A_coefficient"), + getvar("neutral_constraints_B_coefficient"), + getvar("neutral_constraints_C_coefficient"), getvar("time_for_run"), getvar("step_counter"), getvar("dt"), getvar("failure_counter"), getvar("failure_caused_by"), getvar("limit_caused_by"), @@ -1143,9 +1287,8 @@ function setup_dfns_io(prefix, binary_format, boundary_distributions, r, z, vper ### create variables for time-dependent quantities and store them ### ### in a struct for later access ### io_dfns = define_dynamic_dfn_variables!( - fid, r, z, vperp, vpa, vzeta, vr, vz, composition.n_ion_species, - composition.n_neutral_species, parallel_io, external_source_settings, - evolve_density, evolve_upar, evolve_ppar) + fid, r, z, vperp, vpa, vzeta, vr, vz, composition, parallel_io, + external_source_settings, evolve_density, evolve_upar, evolve_ppar) close(fid) @@ -1193,6 +1336,12 @@ function reopen_dfns_io(file_info) getvar("external_source_neutral_momentum_amplitude"), getvar("external_source_neutral_pressure_amplitude"), getvar("external_source_neutral_controller_integral"), + getvar("ion_constraints_A_coefficient"), + getvar("ion_constraints_B_coefficient"), + getvar("ion_constraints_C_coefficient"), + getvar("neutral_constraints_A_coefficient"), + getvar("neutral_constraints_B_coefficient"), + getvar("neutral_constraints_C_coefficient"), getvar("time_for_run"), getvar("step_counter"), getvar("dt"), getvar("failure_counter"), getvar("failure_caused_by"), @@ -1229,11 +1378,11 @@ function append_to_dynamic_var end end """ -write time-dependent moments data to the binary output file +write time-dependent moments data for ions and neutrals to the binary output file """ -function write_moments_data_to_binary(moments, fields, t, n_ion_species, - n_neutral_species, io_or_file_info_moments, t_idx, - time_for_run, t_params, r, z) +function write_all_moments_data_to_binary(moments, fields, t, n_ion_species, + n_neutral_species, io_or_file_info_moments, + t_idx, time_for_run, t_params, r, z) @serial_region begin # Only read/write from first process in each 'block' @@ -1250,145 +1399,228 @@ function write_moments_data_to_binary(moments, fields, t, n_ion_species, # add the time for this time slice to the hdf5 file append_to_dynamic_var(io_moments.time, t, t_idx, parallel_io) + write_em_fields_data_to_binary(fields, io_moments, t_idx, r, z) + + write_ion_moments_data_to_binary(moments, n_ion_species, io_moments, + t_idx, r, z) + + write_neutral_moments_data_to_binary(moments, n_neutral_species, + io_moments, t_idx, r, z) + + append_to_dynamic_var(io_moments.time_for_run, time_for_run, t_idx, parallel_io) + append_to_dynamic_var(io_moments.step_counter, t_params.step_counter[], t_idx, parallel_io) + append_to_dynamic_var(io_moments.dt, t_params.dt_before_output[], t_idx, parallel_io) + append_to_dynamic_var(io_moments.failure_counter, t_params.failure_counter[], t_idx, parallel_io) + append_to_dynamic_var(io_moments.failure_caused_by, t_params.failure_caused_by, + t_idx, parallel_io, length(t_params.failure_caused_by); + only_root=true) + append_to_dynamic_var(io_moments.limit_caused_by, t_params.limit_caused_by, t_idx, + parallel_io, length(t_params.limit_caused_by); + only_root=true) + append_to_dynamic_var(io_moments.dt_before_last_fail, + t_params.dt_before_last_fail[], t_idx, parallel_io) + + closefile && close(io_moments.fid) + end + + return nothing +end + +""" +write time-dependent EM fields data to the binary output file + +Note: should only be called from within a function that (re-)opens the output file. +""" +function write_em_fields_data_to_binary(fields, io_moments::io_moments_info, t_idx, + r, z) + @serial_region begin + # Only read/write from first process in each 'block' + + parallel_io = io_moments.parallel_io + # add the electrostatic potential and electric field components at this time slice to the hdf5 file append_to_dynamic_var(io_moments.phi, fields.phi, t_idx, parallel_io, z, r) append_to_dynamic_var(io_moments.Er, fields.Er, t_idx, parallel_io, z, r) append_to_dynamic_var(io_moments.Ez, fields.Ez, t_idx, parallel_io, z, r) + end + + return nothing +end + +""" +write time-dependent moments data for ions to the binary output file + +Note: should only be called from within a function that (re-)opens the output file. +""" +function write_ion_moments_data_to_binary(moments, n_ion_species, + io_moments::io_moments_info, t_idx, r, z) + @serial_region begin + # Only read/write from first process in each 'block' + + parallel_io = io_moments.parallel_io # add the density data at this time slice to the output file - append_to_dynamic_var(io_moments.density, moments.charged.dens, t_idx, + append_to_dynamic_var(io_moments.density, moments.ion.dens, t_idx, parallel_io, z, r, n_ion_species) - append_to_dynamic_var(io_moments.parallel_flow, moments.charged.upar, t_idx, + append_to_dynamic_var(io_moments.parallel_flow, moments.ion.upar, t_idx, parallel_io, z, r, n_ion_species) - append_to_dynamic_var(io_moments.parallel_pressure, moments.charged.ppar, t_idx, + append_to_dynamic_var(io_moments.parallel_pressure, moments.ion.ppar, t_idx, parallel_io, z, r, n_ion_species) - append_to_dynamic_var(io_moments.perpendicular_pressure, moments.charged.pperp, t_idx, + append_to_dynamic_var(io_moments.perpendicular_pressure, moments.ion.pperp, t_idx, parallel_io, z, r, n_ion_species) - append_to_dynamic_var(io_moments.parallel_heat_flux, moments.charged.qpar, t_idx, + append_to_dynamic_var(io_moments.parallel_heat_flux, moments.ion.qpar, t_idx, parallel_io, z, r, n_ion_species) - append_to_dynamic_var(io_moments.thermal_speed, moments.charged.vth, t_idx, + append_to_dynamic_var(io_moments.thermal_speed, moments.ion.vth, t_idx, parallel_io, z, r, n_ion_species) - append_to_dynamic_var(io_moments.entropy_production, moments.charged.dSdt, t_idx, + append_to_dynamic_var(io_moments.entropy_production, moments.ion.dSdt, t_idx, parallel_io, z, r, n_ion_species) if z.irank == 0 # lower wall append_to_dynamic_var(io_moments.chodura_integral_lower, - moments.charged.chodura_integral_lower, t_idx, + moments.ion.chodura_integral_lower, t_idx, parallel_io, r, n_ion_species) elseif io_moments.chodura_integral_lower !== nothing append_to_dynamic_var(io_moments.chodura_integral_lower, - moments.charged.chodura_integral_lower, t_idx, + moments.ion.chodura_integral_lower, t_idx, parallel_io, 0, n_ion_species) end if z.irank == z.nrank - 1 # upper wall append_to_dynamic_var(io_moments.chodura_integral_upper, - moments.charged.chodura_integral_upper, t_idx, + moments.ion.chodura_integral_upper, t_idx, parallel_io, r, n_ion_species) elseif io_moments.chodura_integral_upper !== nothing append_to_dynamic_var(io_moments.chodura_integral_upper, - moments.charged.chodura_integral_upper, t_idx, + moments.ion.chodura_integral_upper, t_idx, parallel_io, 0, n_ion_species) end if io_moments.external_source_amplitude !== nothing append_to_dynamic_var(io_moments.external_source_amplitude, - moments.charged.external_source_amplitude, t_idx, + moments.ion.external_source_amplitude, t_idx, parallel_io, z, r) if moments.evolve_density append_to_dynamic_var(io_moments.external_source_density_amplitude, - moments.charged.external_source_density_amplitude, + moments.ion.external_source_density_amplitude, t_idx, parallel_io, z, r) end if moments.evolve_upar append_to_dynamic_var(io_moments.external_source_momentum_amplitude, - moments.charged.external_source_momentum_amplitude, + moments.ion.external_source_momentum_amplitude, t_idx, parallel_io, z, r) end if moments.evolve_ppar append_to_dynamic_var(io_moments.external_source_pressure_amplitude, - moments.charged.external_source_pressure_amplitude, + moments.ion.external_source_pressure_amplitude, t_idx, parallel_io, z, r) end end if io_moments.external_source_controller_integral !== nothing - if size(moments.charged.external_source_controller_integral) == (1,1) + if size(moments.ion.external_source_controller_integral) == (1,1) append_to_dynamic_var(io_moments.external_source_controller_integral, - moments.charged.external_source_controller_integral[1,1], + moments.ion.external_source_controller_integral[1,1], t_idx, parallel_io) else append_to_dynamic_var(io_moments.external_source_controller_integral, - moments.charged.external_source_controller_integral, + moments.ion.external_source_controller_integral, t_idx, parallel_io, z, r) end end - if n_neutral_species > 0 - append_to_dynamic_var(io_moments.density_neutral, moments.neutral.dens, t_idx, - parallel_io, z, r, n_neutral_species) - append_to_dynamic_var(io_moments.uz_neutral, moments.neutral.uz, t_idx, + if moments.evolve_density || moments.evolve_upar || moments.evolve_ppar + append_to_dynamic_var(io_moments.ion_constraints_A_coefficient, + moments.ion.constraints_A_coefficient, t_idx, + parallel_io, z, r, n_ion_species) + append_to_dynamic_var(io_moments.ion_constraints_B_coefficient, + moments.ion.constraints_B_coefficient, t_idx, + parallel_io, z, r, n_ion_species) + append_to_dynamic_var(io_moments.ion_constraints_C_coefficient, + moments.ion.constraints_C_coefficient, t_idx, + parallel_io, z, r, n_ion_species) + end + end + + return nothing +end + +""" +write time-dependent moments data for neutrals to the binary output file + +Note: should only be called from within a function that (re-)opens the output file. +""" +function write_neutral_moments_data_to_binary(moments, n_neutral_species, + io_moments::io_moments_info, t_idx, r, z) + if n_neutral_species ≤ 0 + return nothing + end + + @serial_region begin + # Only read/write from first process in each 'block' + + parallel_io = io_moments.parallel_io + + append_to_dynamic_var(io_moments.density_neutral, moments.neutral.dens, t_idx, + parallel_io, z, r, n_neutral_species) + append_to_dynamic_var(io_moments.uz_neutral, moments.neutral.uz, t_idx, + parallel_io, z, r, n_neutral_species) + append_to_dynamic_var(io_moments.pz_neutral, moments.neutral.pz, t_idx, + parallel_io, z, r, n_neutral_species) + append_to_dynamic_var(io_moments.qz_neutral, moments.neutral.qz, t_idx, + parallel_io, z, r, n_neutral_species) + append_to_dynamic_var(io_moments.thermal_speed_neutral, moments.neutral.vth, + t_idx, parallel_io, z, r, n_neutral_species) + + if io_moments.external_source_neutral_amplitude !== nothing + append_to_dynamic_var(io_moments.external_source_neutral_amplitude, + moments.neutral.external_source_amplitude, t_idx, + parallel_io, z, r) + if moments.evolve_density + append_to_dynamic_var(io_moments.external_source_neutral_density_amplitude, + moments.neutral.external_source_density_amplitude, + t_idx, parallel_io, z, r) + end + if moments.evolve_upar + append_to_dynamic_var(io_moments.external_source_neutral_momentum_amplitude, + moments.neutral.external_source_momentum_amplitude, + t_idx, parallel_io, z, r) + end + if moments.evolve_ppar + append_to_dynamic_var(io_moments.external_source_neutral_pressure_amplitude, + moments.neutral.external_source_pressure_amplitude, + t_idx, parallel_io, z, r) + end + end + if io_moments.external_source_neutral_controller_integral !== nothing + if size(moments.neutral.external_source_neutral_controller_integral) == (1,1) + append_to_dynamic_var(io_moments.external_source_neutral_controller_integral, + moments.neutral.external_source_controller_integral[1,1], + t_idx, parallel_io) + else + append_to_dynamic_var(io_moments.external_source_neutral_controller_integral, + moments.neutral.external_source_controller_integral, + t_idx, parallel_io, z, r) + end + end + if moments.evolve_density || moments.evolve_upar || moments.evolve_ppar + append_to_dynamic_var(io_moments.neutral_constraints_A_coefficient, + moments.neutral.constraints_A_coefficient, t_idx, parallel_io, z, r, n_neutral_species) - append_to_dynamic_var(io_moments.pz_neutral, moments.neutral.pz, t_idx, + append_to_dynamic_var(io_moments.neutral_constraints_B_coefficient, + moments.neutral.constraints_B_coefficient, t_idx, parallel_io, z, r, n_neutral_species) - append_to_dynamic_var(io_moments.qz_neutral, moments.neutral.qz, t_idx, + append_to_dynamic_var(io_moments.neutral_constraints_C_coefficient, + moments.neutral.constraints_C_coefficient, t_idx, parallel_io, z, r, n_neutral_species) - append_to_dynamic_var(io_moments.thermal_speed_neutral, moments.neutral.vth, - t_idx, parallel_io, z, r, n_neutral_species) - - if io_moments.external_source_neutral_amplitude !== nothing - append_to_dynamic_var(io_moments.external_source_neutral_amplitude, - moments.neutral.external_source_amplitude, t_idx, - parallel_io, z, r) - if moments.evolve_density - append_to_dynamic_var(io_moments.external_source_neutral_density_amplitude, - moments.neutral.external_source_density_amplitude, - t_idx, parallel_io, z, r) - end - if moments.evolve_upar - append_to_dynamic_var(io_moments.external_source_neutral_momentum_amplitude, - moments.neutral.external_source_momentum_amplitude, - t_idx, parallel_io, z, r) - end - if moments.evolve_ppar - append_to_dynamic_var(io_moments.external_source_neutral_pressure_amplitude, - moments.neutral.external_source_pressure_amplitude, - t_idx, parallel_io, z, r) - end - end - if io_moments.external_source_neutral_controller_integral !== nothing - if size(moments.neutral.external_source_neutral_controller_integral) == (1,1) - append_to_dynamic_var(io_moments.external_source_neutral_controller_integral, - moments.neutral.external_source_controller_integral[1,1], - t_idx, parallel_io) - else - append_to_dynamic_var(io_moments.external_source_neutral_controller_integral, - moments.neutral.external_source_controller_integral, - t_idx, parallel_io, z, r) - end - end end - - append_to_dynamic_var(io_moments.time_for_run, time_for_run, t_idx, parallel_io) - append_to_dynamic_var(io_moments.step_counter, t_params.step_counter[], t_idx, parallel_io) - append_to_dynamic_var(io_moments.dt, t_params.dt_before_output[], t_idx, parallel_io) - append_to_dynamic_var(io_moments.failure_counter, t_params.failure_counter[], t_idx, parallel_io) - append_to_dynamic_var(io_moments.failure_caused_by, t_params.failure_caused_by, - t_idx, parallel_io, length(t_params.failure_caused_by); - only_root=true) - append_to_dynamic_var(io_moments.limit_caused_by, t_params.limit_caused_by, t_idx, - parallel_io, length(t_params.limit_caused_by); - only_root=true) - append_to_dynamic_var(io_moments.dt_before_last_fail, - t_params.dt_before_last_fail[], t_idx, parallel_io) - - closefile && close(io_moments.fid) end + return nothing end """ -write time-dependent distribution function data to the binary output file +write time-dependent distribution function data for ions and neutrals to the +binary output file """ -function write_dfns_data_to_binary(ff, ff_neutral, moments, fields, t, n_ion_species, - n_neutral_species, io_or_file_info_dfns, t_idx, - time_for_run, t_params, r, z, vperp, vpa, vzeta, vr, - vz) +function write_all_dfns_data_to_binary(pdf, moments, fields, t, n_ion_species, + n_neutral_species, io_or_file_info_dfns, t_idx, + time_for_run, t_params, r, z, vperp, vpa, vzeta, vr, + vz) @serial_region begin # Only read/write from first process in each 'block' @@ -1402,21 +1634,56 @@ function write_dfns_data_to_binary(ff, ff_neutral, moments, fields, t, n_ion_spe # Write the moments for this time slice to the output file. # This also updates the time. - write_moments_data_to_binary(moments, fields, t, n_ion_species, n_neutral_species, - io_dfns.io_moments, t_idx, time_for_run, t_params, r, - z) + write_all_moments_data_to_binary(moments, fields, t, n_ion_species, + n_neutral_species, io_dfns.io_moments, t_idx, + time_for_run, t_params, r, z) + + # add the distribution function data at this time slice to the output file + write_ion_dfns_data_to_binary(pdf.ion.norm, n_ion_species, io_dfns, t_idx, r, z, + vperp, vpa) + write_neutral_dfns_data_to_binary(pdf.neutral.norm, n_neutral_species, io_dfns, + t_idx, r, z, vzeta, vr, vz) + + closefile && close(io_dfns.fid) + end + return nothing +end + +""" +write time-dependent distribution function data for ions to the binary output file + +Note: should only be called from within a function that (re-)opens the output file. +""" +function write_ion_dfns_data_to_binary(ff, n_ion_species, io_dfns::io_dfns_info, + t_idx, r, z, vperp, vpa) + @serial_region begin + # Only read/write from first process in each 'block' parallel_io = io_dfns.parallel_io - # add the distribution function data at this time slice to the output file append_to_dynamic_var(io_dfns.f, ff, t_idx, parallel_io, vpa, vperp, z, r, n_ion_species) + end + return nothing +end + +""" +write time-dependent distribution function data for neutrals to the binary output file + +Note: should only be called from within a function that (re-)opens the output file. +""" +function write_neutral_dfns_data_to_binary(ff_neutral, n_neutral_species, + io_dfns::io_dfns_info, t_idx, r, z, vzeta, vr, + vz) + @serial_region begin + # Only read/write from first process in each 'block' + + parallel_io = io_dfns.parallel_io + if n_neutral_species > 0 append_to_dynamic_var(io_dfns.f_neutral, ff_neutral, t_idx, parallel_io, vz, vr, vzeta, z, r, n_neutral_species) end - - closefile && close(io_dfns.fid) end return nothing end @@ -1455,9 +1722,9 @@ include("file_io_hdf5.jl") """ """ -function write_data_to_ascii(moments, fields, vpa, vperp, z, r, t, n_ion_species, +function write_data_to_ascii(pdf, moments, fields, vpa, vperp, z, r, t, n_ion_species, n_neutral_species, ascii_io::Union{ascii_ios,Nothing}) - if ascii_io === nothing || ascii_io.moments_charged === nothing + if ascii_io === nothing || ascii_io.moments_ion === nothing # ascii I/O is disabled return nothing end @@ -1465,8 +1732,8 @@ function write_data_to_ascii(moments, fields, vpa, vperp, z, r, t, n_ion_species @serial_region begin # Only read/write from first process in each 'block' - #write_f_ascii(ff, z, vpa, t, ascii_io.ff) - write_moments_charged_ascii(moments.charged, z, r, t, n_ion_species, ascii_io.moments_charged) + write_f_ascii(pdf, z, vpa, t, ascii_io.ff) + write_moments_ion_ascii(moments.ion, z, r, t, n_ion_species, ascii_io.moments_ion) if n_neutral_species > 0 write_moments_neutral_ascii(moments.neutral, z, r, t, n_neutral_species, ascii_io.moments_neutral) end @@ -1483,27 +1750,28 @@ function write_f_ascii(f, z, vpa, t, ascii_io) # Only read/write from first process in each 'block' @inbounds begin - n_species = size(f,3) - for is ∈ 1:n_species + #n_species = size(f,3) + #for is ∈ 1:n_species for j ∈ 1:vpa.n for i ∈ 1:z.n - println(ascii_io,"t: ", t, " spec: ", is, ", z: ", z.grid[i], - ", vpa: ", vpa.grid[j], ", f: ", f[i,j,is]) + println(ascii_io,"t: ", t, " z: ", z.grid[i], + " vpa: ", vpa.grid[j], " fion: ", f.ion.norm[i,j,1], + " fneutral: ", f.neutral.norm[i,j,1]) end println(ascii_io) end println(ascii_io) - end - println(ascii_io) + #end + #println(ascii_io) end end return nothing end """ -write moments of the charged species distribution function f at this time slice +write moments of the ion species distribution function f at this time slice """ -function write_moments_charged_ascii(mom, z, r, t, n_species, ascii_io) +function write_moments_ion_ascii(mom, z, r, t, n_species, ascii_io) @serial_region begin # Only read/write from first process in each 'block' @@ -1556,7 +1824,8 @@ function write_fields_ascii(flds, z, r, t, ascii_io) @inbounds begin for ir ∈ 1:r.n for iz ∈ 1:z.n - println(ascii_io,"t: ", t, " r: ", r.grid[ir]," z: ", z.grid[iz], " phi: ", flds.phi[iz,ir]) + println(ascii_io,"t: ", t, " r: ", r.grid[ir]," z: ", z.grid[iz], " phi: ", flds.phi[iz,ir], + " Ez: ", flds.Ez[iz,ir]) end end end @@ -1704,9 +1973,9 @@ function debug_dump(vz::coordinate, vr::coordinate, vzeta::coordinate, vpa::coor debug_output_file.label[debug_output_counter[]] = label # add the distribution function data at this time slice to the netcdf file if ff === nothing - debug_output_file.dfns.charged_f[:,:,:,:,:,debug_output_counter[]] = 0.0 + debug_output_file.dfns.ion_f[:,:,:,:,:,debug_output_counter[]] = 0.0 else - debug_output_file.dfns.charged_f[:,:,:,:,:,debug_output_counter[]] = ff + debug_output_file.dfns.ion_f[:,:,:,:,:,debug_output_counter[]] = ff end # add the moments data at this time slice to the netcdf file if dens === nothing diff --git a/moment_kinetics/src/file_io_hdf5.jl b/moment_kinetics/src/file_io_hdf5.jl index 6c9de806e..c40dea9d5 100644 --- a/moment_kinetics/src/file_io_hdf5.jl +++ b/moment_kinetics/src/file_io_hdf5.jl @@ -80,9 +80,9 @@ end # HDF5.H5DataStore is the supertype for HDF5.File and HDF5.Group function write_single_value!(file_or_group::HDF5.H5DataStore, name, data::Union{Number, AbstractString, AbstractArray{T,N}}, - coords...; parallel_io, n_ion_species=nothing, - n_neutral_species=nothing, description=nothing, - units=nothing) where {T,N} + coords::Union{coordinate,mk_int}...; parallel_io, + n_ion_species=nothing, n_neutral_species=nothing, + description=nothing, units=nothing) where {T,N} if isa(data, Union{Number, AbstractString}) file_or_group[name] = data if description !== nothing diff --git a/moment_kinetics/src/fokker_planck.jl b/moment_kinetics/src/fokker_planck.jl index 5b2922328..0352f9a1f 100644 --- a/moment_kinetics/src/fokker_planck.jl +++ b/moment_kinetics/src/fokker_planck.jl @@ -52,7 +52,7 @@ using ..velocity_moments: integrate_over_vspace using ..velocity_moments: get_density, get_upar, get_ppar, get_pperp, get_qpar, get_pressure, get_rmom using ..looping using ..input_structs: fkpl_collisions_input, set_defaults_and_check_section! -using ..reference_parameters: get_reference_collision_frequency +using ..reference_parameters: get_reference_collision_frequency_ii using ..fokker_planck_calculus: init_Rosenbluth_potential_integration_weights! using ..fokker_planck_calculus: init_Rosenbluth_potential_boundary_integration_weights! using ..fokker_planck_calculus: allocate_boundary_integration_weights @@ -82,7 +82,7 @@ frequency_option = "manual" """ function setup_fkpl_collisions_input(toml_input::Dict, reference_params) # get reference collision frequency (note factor of 1/2 due to definition choices) - nuii_fkpl_default = 0.5*get_reference_collision_frequency(reference_params) + nuii_fkpl_default = 0.5*get_reference_collision_frequency_ii(reference_params) # read the input toml and specify a sensible default input_section = set_defaults_and_check_section!(toml_input, "fokker_planck_collisions", # begin default inputs (as kwargs) diff --git a/moment_kinetics/src/force_balance.jl b/moment_kinetics/src/force_balance.jl index 562f59972..902c3901e 100644 --- a/moment_kinetics/src/force_balance.jl +++ b/moment_kinetics/src/force_balance.jl @@ -23,14 +23,14 @@ function force_balance!(pflx, density_out, fvec, moments, fields, collisions, dt upar = fvec.upar @loop_s_r_z is ir iz begin pflx[iz,ir,is] = density[iz,ir,is]*upar[iz,ir,is] - - dt*(moments.charged.dppar_dz[iz,ir,is] + - upar[iz,ir,is]*upar[iz,ir,is]*moments.charged.ddens_dz_upwind[iz,ir,is] + - 2.0*density[iz,ir,is]*upar[iz,ir,is]*moments.charged.dupar_dz_upwind[iz,ir,is] - + dt*(moments.ion.dppar_dz[iz,ir,is] + + upar[iz,ir,is]*upar[iz,ir,is]*moments.ion.ddens_dz_upwind[iz,ir,is] + + 2.0*density[iz,ir,is]*upar[iz,ir,is]*moments.ion.dupar_dz_upwind[iz,ir,is] - 0.5*geometry.bzed[iz,ir]*fields.Ez[iz,ir]*density[iz,ir,is]) end if ion_source_settings.active && false - source_amplitude = moments.charged.external_source_momentum_amplitude + source_amplitude = moments.ion.external_source_momentum_amplitude @loop_s_r_z is ir iz begin pflx[iz,ir,is] += dt * source_amplitude[iz,ir] @@ -38,10 +38,10 @@ function force_balance!(pflx, density_out, fvec, moments, fields, collisions, dt end # Ad-hoc diffusion to stabilise numerics... - diffusion_coefficient = num_diss_params.moment_dissipation_coefficient + diffusion_coefficient = num_diss_params.ion.moment_dissipation_coefficient if diffusion_coefficient > 0.0 @loop_s_r_z is ir iz begin - pflx[iz,ir,is] += dt*diffusion_coefficient*moments.charged.d2upar_dz2[iz,ir,is]*density[iz,ir,is] + pflx[iz,ir,is] += dt*diffusion_coefficient*moments.ion.d2upar_dz2[iz,ir,is]*density[iz,ir,is] end end @@ -92,7 +92,7 @@ function neutral_force_balance!(pflx, density_out, fvec, moments, fields, collis end # Ad-hoc diffusion to stabilise numerics... - diffusion_coefficient = num_diss_params.moment_dissipation_coefficient + diffusion_coefficient = num_diss_params.neutral.moment_dissipation_coefficient if diffusion_coefficient > 0.0 @loop_sn_r_z isn ir iz begin pflx[iz,ir,isn] += dt*diffusion_coefficient*moments.neutral.d2uz_dz2[iz,ir,isn]*density[iz,ir,isn] diff --git a/moment_kinetics/src/initial_conditions.jl b/moment_kinetics/src/initial_conditions.jl index 8c5ab2520..7f724c6bc 100644 --- a/moment_kinetics/src/initial_conditions.jl +++ b/moment_kinetics/src/initial_conditions.jl @@ -4,8 +4,6 @@ module initial_conditions export allocate_pdf_and_moments export init_pdf_and_moments! -export enforce_boundary_conditions! -export enforce_neutral_boundary_conditions! # functional testing export create_boundary_distributions @@ -17,18 +15,16 @@ using SpecialFunctions: erfc using ..type_definitions: mk_float, mk_int using ..array_allocation: allocate_float, allocate_shared_float using ..bgk: init_bgk_pdf! +using ..boundary_conditions: vpagrid_to_dzdt using ..communication -using ..calculus: reconcile_element_boundaries_MPI! -using ..coordinates: coordinate using ..external_sources using ..interpolation: interpolate_to_grid_1d! using ..looping -using ..moment_kinetics_structs: scratch_pdf +using ..moment_kinetics_structs: scratch_pdf, pdf_substruct, + pdf_struct, moments_struct, boundary_distributions_struct using ..velocity_moments: integrate_over_vspace, integrate_over_neutral_vspace -using ..velocity_moments: integrate_over_positive_vpa, integrate_over_negative_vpa using ..velocity_moments: integrate_over_positive_vz, integrate_over_negative_vz -using ..velocity_moments: create_moments_charged, create_moments_neutral, update_qpar! -using ..velocity_moments: moments_charged_substruct, moments_neutral_substruct +using ..velocity_moments: create_moments_ion, create_moments_neutral, update_qpar! using ..velocity_moments: update_neutral_density!, update_neutral_pz!, update_neutral_pr!, update_neutral_pzeta! using ..velocity_moments: update_neutral_uz!, update_neutral_ur!, update_neutral_uzeta!, update_neutral_qz! using ..velocity_moments: update_ppar!, update_upar!, update_density!, update_pperp!, update_vth!, reset_moments_status! @@ -37,53 +33,12 @@ using ..manufactured_solns: manufactured_solutions using MPI -""" -""" -struct pdf_substruct{n_distribution} - norm::MPISharedArray{mk_float,n_distribution} - buffer::MPISharedArray{mk_float,n_distribution} # for collision operator terms when pdfs must be interpolated onto different velocity space grids, and for gyroaveraging -end - -# struct of structs neatly contains i+n info? -struct pdf_struct - #charged particles: s + r + z + vperp + vpa - charged::pdf_substruct{5} - #neutral particles: s + r + z + vzeta + vr + vz - neutral::pdf_substruct{6} -end - -struct moments_struct - charged::moments_charged_substruct - neutral::moments_neutral_substruct - # flag that indicates if the density should be evolved via continuity equation - evolve_density::Bool - # flag that indicates if particle number should be conserved for each species - # effects like ionisation or net particle flux from the domain would lead to - # non-conservation - particle_number_conserved::Bool - # flag that indicates if exact particle conservation should be enforced - enforce_conservation::Bool - # flag that indicates if the parallel flow should be evolved via force balance - evolve_upar::Bool - # flag that indicates if the parallel pressure should be evolved via the energy equation - evolve_ppar::Bool -end - -struct boundary_distributions_struct - # knudsen cosine distribution for imposing the neutral wall boundary condition - knudsen::MPISharedArray{mk_float,3} - # charged particle r boundary values (vpa,vperp,z,r,s) - pdf_rboundary_charged::MPISharedArray{mk_float,5} - # neutral particle r boundary values (vz,vr,vzeta,z,r,s) - pdf_rboundary_neutral::MPISharedArray{mk_float,6} -end - """ Creates the structs for the pdf and the velocity-space moments """ function allocate_pdf_and_moments(composition, r, z, vperp, vpa, vzeta, vr, vz, evolve_moments, collisions, external_source_settings, - numerical_dissipation) + num_diss_params) pdf = create_pdf(composition, r, z, vperp, vpa, vzeta, vr, vz) # create the 'moments' struct that contains various v-space moments and other @@ -91,14 +46,14 @@ function allocate_pdf_and_moments(composition, r, z, vperp, vpa, vzeta, vr, vz, # the time-dependent entries are not initialised. # moments arrays have same r and z grids for both ion and neutral species # and so are included in the same struct - charged = create_moments_charged(z.n, r.n, composition.n_ion_species, + ion = create_moments_ion(z.n, r.n, composition.n_ion_species, evolve_moments.density, evolve_moments.parallel_flow, evolve_moments.parallel_pressure, external_source_settings.ion, - numerical_dissipation) + num_diss_params) neutral = create_moments_neutral(z.n, r.n, composition.n_neutral_species, evolve_moments.density, evolve_moments.parallel_flow, evolve_moments.parallel_pressure, external_source_settings.neutral, - numerical_dissipation) + num_diss_params) if abs(collisions.ionization) > 0.0 || z.bc == "wall" # if ionization collisions are included or wall BCs are enforced, then particle @@ -109,7 +64,7 @@ function allocate_pdf_and_moments(composition, r, z, vperp, vpa, vzeta, vr, vz, particle_number_conserved = true end - moments = moments_struct(charged, neutral, evolve_moments.density, + moments = moments_struct(ion, neutral, evolve_moments.density, particle_number_conserved, evolve_moments.conservation, evolve_moments.parallel_flow, @@ -126,14 +81,13 @@ Allocate arrays for pdfs """ function create_pdf(composition, r, z, vperp, vpa, vzeta, vr, vz) # allocate pdf arrays - pdf_charged_norm = allocate_shared_float(vpa.n, vperp.n, z.n, r.n, composition.n_ion_species) - # buffer array is for ion-neutral collisions, not for storing charged pdf - pdf_charged_buffer = allocate_shared_float(vpa.n, vperp.n, z.n, r.n, composition.n_neutral_species) # n.b. n_species is n_neutral_species here + pdf_ion_norm = allocate_shared_float(vpa.n, vperp.n, z.n, r.n, composition.n_ion_species) + # buffer array is for ion-neutral collisions, not for storing ion pdf + pdf_ion_buffer = allocate_shared_float(vpa.n, vperp.n, z.n, r.n, composition.n_neutral_species) # n.b. n_species is n_neutral_species here pdf_neutral_norm = allocate_shared_float(vz.n, vr.n, vzeta.n, z.n, r.n, composition.n_neutral_species) # buffer array is for neutral-ion collisions, not for storing neutral pdf pdf_neutral_buffer = allocate_shared_float(vz.n, vr.n, vzeta.n, z.n, r.n, composition.n_ion_species) - - return pdf_struct(pdf_substruct(pdf_charged_norm, pdf_charged_buffer), + return pdf_struct(pdf_substruct(pdf_ion_norm, pdf_ion_buffer), pdf_substruct(pdf_neutral_norm, pdf_neutral_buffer)) end @@ -156,33 +110,52 @@ function init_pdf_and_moments!(pdf, moments, boundary_distributions, geometry, n_ion_species = composition.n_ion_species n_neutral_species = composition.n_neutral_species @serial_region begin - # initialise the density profile - init_density!(moments.charged.dens, z, r, species.charged, n_ion_species) - # initialise the parallel flow profile - init_upar!(moments.charged.upar, z, r, species.charged, n_ion_species) - # initialise the parallel thermal speed profile - init_vth!(moments.charged.vth, z, r, species.charged, n_ion_species) + # initialise the ion density profile + init_density!(moments.ion.dens, z, r, species.ion, n_ion_species) + # initialise the ion parallel flow profile + init_upar!(moments.ion.upar, z, r, species.ion, n_ion_species) + # initialise the ion parallel thermal speed profile + init_vth!(moments.ion.vth, z, r, species.ion, n_ion_species) + @. moments.ion.ppar = 0.5 * moments.ion.dens * moments.ion.vth^2 # initialise pressures assuming isotropic distribution - @. moments.charged.ppar = 0.5 * moments.charged.dens * moments.charged.vth^2 - @. moments.charged.pperp = moments.charged.ppar + @. moments.ion.ppar = 0.5 * moments.ion.dens * moments.ion.vth^2 + @. moments.ion.pperp = moments.ion.ppar + if moments.evolve_density || moments.evolve_upar || moments.evolve_ppar + @. moments.ion.constraints_A_coefficient = 1.0 + @. moments.ion.constraints_B_coefficient = 0.0 + @. moments.ion.constraints_C_coefficient = 0.0 + end if n_neutral_species > 0 - #neutral particles + # initialise the neutral density profile init_density!(moments.neutral.dens, z, r, species.neutral, n_neutral_species) + # initialise the z-component of the neutral flow init_uz!(moments.neutral.uz, z, r, species.neutral, n_neutral_species) + # initialise the r-component of the neutral flow init_ur!(moments.neutral.ur, z, r, species.neutral, n_neutral_species) + # initialise the zeta-component of the neutral flow init_uzeta!(moments.neutral.uzeta, z, r, species.neutral, n_neutral_species) + # initialise the neutral thermal speed init_vth!(moments.neutral.vth, z, r, species.neutral, n_neutral_species) + # calculate the z-component of the neutral pressure @. moments.neutral.pz = 0.5 * moments.neutral.dens * moments.neutral.vth^2 + # calculate the total neutral pressure @. moments.neutral.ptot = 1.5 * moments.neutral.dens * moments.neutral.vth^2 + if moments.evolve_density || moments.evolve_upar || moments.evolve_ppar + @. moments.neutral.constraints_A_coefficient = 1.0 + @. moments.neutral.constraints_B_coefficient = 0.0 + @. moments.neutral.constraints_C_coefficient = 0.0 + end end end - moments.charged.dens_updated .= true - moments.charged.upar_updated .= true - moments.charged.ppar_updated .= true + # reflect the fact that the ion moments have now been updated + moments.ion.dens_updated .= true + moments.ion.upar_updated .= true + moments.ion.ppar_updated .= true + # account for the fact that the neutral moments have now been updated moments.neutral.dens_updated .= true moments.neutral.uz_updated .= true moments.neutral.pz_updated .= true - # create and initialise the normalised particle distribution function (pdf) + # create and initialise the normalised, ion particle distribution function (pdf) # such that ∫dwpa pdf.norm = 1, ∫dwpa wpa * pdf.norm = 0, and ∫dwpa wpa^2 * pdf.norm = 1/2 # note that wpa = vpa - upar, unless moments.evolve_ppar = true, in which case wpa = (vpa - upar)/vth # the definition of pdf.norm changes accordingly from pdf_unnorm / density to pdf_unnorm * vth / density @@ -191,9 +164,9 @@ function init_pdf_and_moments!(pdf, moments, boundary_distributions, geometry, vpa, vzeta, vr, vz, vpa_spectral, vz_spectral, species) begin_s_r_z_region() # calculate the initial parallel heat flux from the initial un-normalised pdf - update_qpar!(moments.charged.qpar, moments.charged.qpar_updated, - moments.charged.dens, moments.charged.upar, moments.charged.vth, - pdf.charged.norm, vpa, vperp, z, r, composition, + update_qpar!(moments.ion.qpar, moments.ion.qpar_updated, + moments.ion.dens, moments.ion.upar, moments.ion.vth, + pdf.ion.norm, vpa, vperp, z, r, composition, moments.evolve_density, moments.evolve_upar, moments.evolve_ppar) initialize_external_source_amplitude!(moments, external_source_settings, vperp, @@ -222,7 +195,7 @@ function init_pdf_and_moments!(pdf, moments, boundary_distributions, geometry, # collision operator will not be calculated before the initial values are written to # file. @serial_region begin - moments.charged.dSdt .= 0.0 + moments.ion.dSdt .= 0.0 end init_boundary_distributions!(boundary_distributions, pdf, vz, vr, vzeta, vpa, vperp, @@ -242,30 +215,30 @@ function initialize_pdf!(pdf, moments, boundary_distributions, composition, r, z for is ∈ 1:composition.n_ion_species, ir ∈ 1:r.n # Add ion contributions to wall flux here. Neutral contributions will be # added in init_neutral_pdf_over_density!() - if species.charged[is].z_IC.initialization_option == "bgk" || species.charged[is].vpa_IC.initialization_option == "bgk" - @views init_bgk_pdf!(pdf.charged.norm[:,1,:,ir,is], 0.0, species.charged[is].initial_temperature, z.grid, z.L, vpa.grid) + if species.ion[is].z_IC.initialization_option == "bgk" || species.ion[is].vpa_IC.initialization_option == "bgk" + @views init_bgk_pdf!(pdf.ion.norm[:,1,:,ir,is], 0.0, species.ion[is].initial_temperature, z.grid, z.L, vpa.grid) else # updates pdf_norm to contain pdf / density, so that ∫dvpa pdf.norm = 1, # ∫dwpa wpa * pdf.norm = 0, and ∫dwpa m_s (wpa/vths)^2 pdf.norm = 1/2 # to machine precision - @views init_charged_pdf_over_density!( - pdf.charged.norm[:,:,:,ir,is], species.charged[is], composition, vpa, vperp, - z, vpa_spectral, moments.charged.dens[:,ir,is], - moments.charged.upar[:,ir,is], moments.charged.ppar[:,ir,is], - moments.charged.vth[:,ir,is], - moments.charged.v_norm_fac[:,ir,is], moments.evolve_density, + @views init_ion_pdf_over_density!( + pdf.ion.norm[:,:,:,ir,is], species.ion[is], composition, vpa, vperp, + z, vpa_spectral, moments.ion.dens[:,ir,is], + moments.ion.upar[:,ir,is], moments.ion.ppar[:,ir,is], + moments.ion.vth[:,ir,is], + moments.ion.v_norm_fac[:,ir,is], moments.evolve_density, moments.evolve_upar, moments.evolve_ppar) end - @views wall_flux_0[ir,is] = -(moments.charged.dens[1,ir,is] * - moments.charged.upar[1,ir,is]) - @views wall_flux_L[ir,is] = moments.charged.dens[end,ir,is] * - moments.charged.upar[end,ir,is] + @views wall_flux_0[ir,is] = -(moments.ion.dens[1,ir,is] * + moments.ion.upar[1,ir,is]) + @views wall_flux_L[ir,is] = moments.ion.dens[end,ir,is] * + moments.ion.upar[end,ir,is] @loop_z iz begin if moments.evolve_ppar - @. pdf.charged.norm[:,:,iz,ir,is] *= moments.charged.vth[iz,ir,is] + @. pdf.ion.norm[:,:,iz,ir,is] *= moments.ion.vth[iz,ir,is] elseif moments.evolve_density == false - @. pdf.charged.norm[:,:,iz,ir,is] *= moments.charged.dens[iz,ir,is] + @. pdf.ion.norm[:,:,iz,ir,is] *= moments.ion.dens[iz,ir,is] end end end @@ -529,7 +502,7 @@ end """ """ -function init_charged_pdf_over_density!(pdf, spec, composition, vpa, vperp, z, +function init_ion_pdf_over_density!(pdf, spec, composition, vpa, vperp, z, vpa_spectral, density, upar, ppar, vth, v_norm_fac, evolve_density, evolve_upar, evolve_ppar) @@ -563,7 +536,7 @@ function init_charged_pdf_over_density!(pdf, spec, composition, vpa, vperp, z, end # Only do this correction for runs without wall bc, because consistency of - # pdf and moments is taken care of by convert_full_f_charged_to_normalised!() + # pdf and moments is taken care of by convert_full_f_ion_to_normalised!() # for wall bc cases. for iz ∈ 1:z.n # densfac = the integral of the pdf over v-space, which should be unity, @@ -623,7 +596,7 @@ function init_charged_pdf_over_density!(pdf, spec, composition, vpa, vperp, z, @. pdf[:,ivperp,iz] *= 1.0 - exp(-vpa.grid^2*inverse_width) end end - # Can use non-shared memory here because `init_charged_pdf_over_density!()` is + # Can use non-shared memory here because `init_ion_pdf_over_density!()` is # called inside a `@serial_region` lower_z_pdf_buffer = allocate_float(vpa.n, vperp.n) upper_z_pdf_buffer = allocate_float(vpa.n, vperp.n) @@ -665,7 +638,7 @@ function init_charged_pdf_over_density!(pdf, spec, composition, vpa, vperp, z, # Get the unnormalised pdf and the moments of the constructed full-f # distribution function (which will be modified from the input moments). - convert_full_f_charged_to_normalised!(pdf, density, upar, ppar, vth, vperp, + convert_full_f_ion_to_normalised!(pdf, density, upar, ppar, vth, vperp, vpa, vpa_spectral, evolve_density, evolve_upar, evolve_ppar) @@ -829,7 +802,7 @@ function init_neutral_pdf_over_density!(pdf, boundary_distributions, spec, compo @. pdf[:,ivr,ivzeta,iz] *= 1.0 - exp(-vz.grid^2*inverse_width) end end - # Can use non-shared memory here because `init_charged_pdf_over_density!()` is + # Can use non-shared memory here because `init_ion_pdf_over_density!()` is # called inside a `@serial_region` lower_z_pdf_buffer = allocate_float(vz.n, vr.n, vzeta.n) upper_z_pdf_buffer = allocate_float(vz.n, vr.n, vzeta.n) @@ -954,31 +927,30 @@ function init_pdf_moments_manufactured_solns!(pdf, moments, vz, vr, vzeta, vpa, #nb manufactured functions not functions of species begin_s_r_z_region() @loop_s_r_z is ir iz begin - moments.charged.dens[iz,ir,is] = densi_func(z.grid[iz],r.grid[ir],0.0) + moments.ion.dens[iz,ir,is] = densi_func(z.grid[iz],r.grid[ir],0.0) @loop_vperp_vpa ivperp ivpa begin - pdf.charged.norm[ivpa,ivperp,iz,ir,is] = dfni_func(vpa.grid[ivpa],vperp.grid[ivperp],z.grid[iz],r.grid[ir],0.0) + pdf.ion.norm[ivpa,ivperp,iz,ir,is] = dfni_func(vpa.grid[ivpa],vperp.grid[ivperp],z.grid[iz],r.grid[ir],0.0) end end # update upar, ppar, qpar, vth consistent with manufactured solns - reset_moments_status!(moments) - update_density!(moments.charged.dens, moments.charged.dens_updated, - pdf.charged.norm, vpa, vperp, z, r, composition) + update_density!(moments.ion.dens, moments.ion.dens_updated, + pdf.ion.norm, vpa, vperp, z, r, composition) # get particle flux - update_upar!(moments.charged.upar, moments.charged.upar_updated, - moments.charged.dens, moments.charged.ppar, pdf.charged.norm, + update_upar!(moments.ion.upar, moments.ion.upar_updated, + moments.ion.dens, moments.ion.ppar, pdf.ion.norm, vpa, vperp, z, r, composition, moments.evolve_density, moments.evolve_ppar) - update_ppar!(moments.charged.ppar, moments.charged.ppar_updated, - moments.charged.dens, moments.charged.upar, pdf.charged.norm, + update_ppar!(moments.ion.ppar, moments.ion.ppar_updated, + moments.ion.dens, moments.ion.upar, pdf.ion.norm, vpa, vperp, z, r, composition, moments.evolve_density, moments.evolve_upar) - update_pperp!(moments.charged.pperp, pdf.charged.norm, vpa, vperp, z, r, composition) - update_qpar!(moments.charged.qpar, moments.charged.qpar_updated, - moments.charged.dens, moments.charged.upar, - moments.charged.vth, pdf.charged.norm, vpa, vperp, z, r, + update_pperp!(moments.ion.pperp, pdf.ion.norm, vpa, vperp, z, r, composition) + update_qpar!(moments.ion.qpar, moments.ion.qpar_updated, + moments.ion.dens, moments.ion.upar, + moments.ion.vth, pdf.ion.norm, vpa, vperp, z, r, composition, moments.evolve_density, moments.evolve_upar, moments.evolve_ppar) - update_vth!(moments.charged.vth, moments.charged.ppar, moments.charged.pperp, moments.charged.dens, vperp, z, r, composition) + update_vth!(moments.ion.vth, moments.ion.ppar, moments.ion.pperp, moments.ion.dens, vperp, z, r, composition) if n_neutral_species > 0 begin_sn_r_z_region() @@ -1036,7 +1008,7 @@ function init_pdf_moments_manufactured_solns!(pdf, moments, vz, vr, vzeta, vpa, begin_sn_r_z_region() @loop_sn_r_z isn ir iz begin # get vth for neutrals - moments.charged.vth[iz,ir,isn] = sqrt(2.0*moments.neutral.ptot[iz,ir,isn]/moments.neutral.dens[iz,ir,isn]) + moments.neutral.vth[iz,ir,isn] = sqrt(2.0*moments.neutral.ptot[iz,ir,isn]/moments.neutral.dens[iz,ir,isn]) end end return nothing @@ -1106,7 +1078,7 @@ function init_knudsen_cosine!(knudsen_cosine, vz, vr, vzeta, vpa, vperp, composi return knudsen_cosine end -function init_rboundary_pdfs!(rboundary_charged, rboundary_neutral, pdf::pdf_struct, vz, +function init_rboundary_pdfs!(rboundary_ion, rboundary_neutral, pdf::pdf_struct, vz, vr, vzeta, vpa, vperp, z, r, composition) n_ion_species = composition.n_ion_species n_neutral_species = composition.n_neutral_species @@ -1114,8 +1086,8 @@ function init_rboundary_pdfs!(rboundary_charged, rboundary_neutral, pdf::pdf_str begin_s_z_region() #do not parallelise r here @loop_s_z_vperp_vpa is iz ivperp ivpa begin - rboundary_charged[ivpa,ivperp,iz,1,is] = pdf.charged.norm[ivpa,ivperp,iz,1,is] - rboundary_charged[ivpa,ivperp,iz,end,is] = pdf.charged.norm[ivpa,ivperp,iz,end,is] + rboundary_ion[ivpa,ivperp,iz,1,is] = pdf.ion.norm[ivpa,ivperp,iz,1,is] + rboundary_ion[ivpa,ivperp,iz,end,is] = pdf.ion.norm[ivpa,ivperp,iz,end,is] end if n_neutral_species > 0 begin_sn_z_region() #do not parallelise r here @@ -1124,7 +1096,7 @@ function init_rboundary_pdfs!(rboundary_charged, rboundary_neutral, pdf::pdf_str rboundary_neutral[ivz,ivr,ivzeta,iz,end,isn] = pdf.neutral.norm[ivz,ivr,ivzeta,iz,end,isn] end end - return rboundary_charged, rboundary_neutral + return rboundary_ion, rboundary_neutral end """ @@ -1141,914 +1113,24 @@ function create_boundary_distributions(vz, vr, vzeta, vpa, vperp, z, composition #depends on T_wall, which has already been set init_knudsen_cosine!(knudsen_cosine, vz, vr, vzeta, vpa, vperp, composition, zero) #initialise fixed-in-time radial boundary condition based on initial condition values - pdf_rboundary_charged = allocate_shared_float(vpa.n, vperp.n, z.n, 2, + pdf_rboundary_ion = allocate_shared_float(vpa.n, vperp.n, z.n, 2, composition.n_ion_species) pdf_rboundary_neutral = allocate_shared_float(vz.n, vr.n, vzeta.n, z.n, 2, composition.n_neutral_species) - return boundary_distributions_struct(knudsen_cosine, pdf_rboundary_charged, pdf_rboundary_neutral) + return boundary_distributions_struct(knudsen_cosine, pdf_rboundary_ion, pdf_rboundary_neutral) end function init_boundary_distributions!(boundary_distributions, pdf, vz, vr, vzeta, vpa, vperp, z, r, composition) #initialise fixed-in-time radial boundary condition based on initial condition values - init_rboundary_pdfs!(boundary_distributions.pdf_rboundary_charged, + init_rboundary_pdfs!(boundary_distributions.pdf_rboundary_ion, boundary_distributions.pdf_rboundary_neutral, pdf, vz, vr, vzeta, vpa, vperp, z, r, composition) return nothing end -""" -enforce boundary conditions in vpa and z on the evolved pdf; -also enforce boundary conditions in z on all separately evolved velocity space moments of the pdf -""" -function enforce_boundary_conditions!(f, f_r_bc, density, upar, ppar, moments, vpa_bc, - z_bc, r_bc, vpa, vperp, z, r, vpa_spectral, vperp_spectral, vpa_adv, vperp_adv, z_adv, r_adv, composition, scratch_dummy, - r_diffusion, vpa_diffusion, vperp_diffusion) - if vpa.n > 1 - begin_s_r_z_vperp_region() - @loop_s_r_z_vperp is ir iz ivperp begin - # enforce the vpa BC - # use that adv.speed independent of vpa - @views enforce_v_boundary_condition_local!(f[:,ivperp,iz,ir,is], vpa_bc, - vpa_adv[is].speed[:,ivperp,iz,ir], vpa_diffusion, - vpa, vpa_spectral) - end - end - if vperp.n > 1 - begin_s_r_z_vpa_region() - @views enforce_vperp_boundary_condition!(f, vperp.bc, vperp, vperp_spectral, - vperp_adv, vperp_diffusion) - end - if z.n > 1 - begin_s_r_vperp_vpa_region() - # enforce the z BC on the evolved velocity space moments of the pdf - @views enforce_z_boundary_condition_moments!(density, moments, z_bc) - @views enforce_z_boundary_condition!(f, density, upar, ppar, moments, z_bc, z_adv, z, - vperp, vpa, composition, - scratch_dummy.buffer_vpavperprs_1, scratch_dummy.buffer_vpavperprs_2, - scratch_dummy.buffer_vpavperprs_3, scratch_dummy.buffer_vpavperprs_4) - - end - if r.n > 1 - begin_s_z_vperp_vpa_region() - @views enforce_r_boundary_condition!(f, f_r_bc, r_bc, r_adv, vpa, vperp, z, r, composition, - scratch_dummy.buffer_vpavperpzs_1, scratch_dummy.buffer_vpavperpzs_2, - scratch_dummy.buffer_vpavperpzs_3, scratch_dummy.buffer_vpavperpzs_4, - r_diffusion) - end -end -function enforce_boundary_conditions!(fvec_out::scratch_pdf, moments, f_r_bc, vpa_bc, - z_bc, r_bc, vpa, vperp, z, r, vpa_spectral, vperp_spectral, vpa_adv, vperp_adv, z_adv, r_adv, composition, scratch_dummy, - r_diffusion, vpa_diffusion, vperp_diffusion) - enforce_boundary_conditions!(fvec_out.pdf, f_r_bc, fvec_out.density, fvec_out.upar, - fvec_out.ppar, moments, vpa_bc, z_bc, r_bc, vpa, vperp, z, r, - vpa_spectral, vperp_spectral, vpa_adv, vperp_adv, z_adv, - r_adv, composition, scratch_dummy, r_diffusion, vpa_diffusion, vperp_diffusion) -end - -""" -enforce boundary conditions on f in r -""" -function enforce_r_boundary_condition!(f::AbstractArray{mk_float,5}, f_r_bc, bc::String, - adv, vpa, vperp, z, r, composition, end1::AbstractArray{mk_float,4}, - end2::AbstractArray{mk_float,4}, buffer1::AbstractArray{mk_float,4}, - buffer2::AbstractArray{mk_float,4}, r_diffusion::Bool) - - nr = r.n - - if r.nelement_global > r.nelement_local - # reconcile internal element boundaries across processes - # & enforce periodicity and external boundaries if needed - @loop_s_z_vperp_vpa is iz ivperp ivpa begin - end1[ivpa,ivperp,iz,is] = f[ivpa,ivperp,iz,1,is] - end2[ivpa,ivperp,iz,is] = f[ivpa,ivperp,iz,nr,is] - end - @views reconcile_element_boundaries_MPI!(f, - end1, end2, buffer1, buffer2, r) - end - - # 'periodic' BC enforces periodicity by taking the average of the boundary points - # enforce the condition if r is local - if bc == "periodic" && r.nelement_global == r.nelement_local - @loop_s_z_vperp_vpa is iz ivperp ivpa begin - f[ivpa,ivperp,iz,1,is] = 0.5*(f[ivpa,ivperp,iz,nr,is]+f[ivpa,ivperp,iz,1,is]) - f[ivpa,ivperp,iz,nr,is] = f[ivpa,ivperp,iz,1,is] - end - end - if bc == "Dirichlet" - zero = 1.0e-10 - # use the old distribution to force the new distribution to have - # consistant-in-time values at the boundary - # with bc = "Dirichlet" and r_diffusion = false - # impose bc for incoming parts of velocity space only (Hyperbolic PDE) - # with bc = "Dirichlet" and r_diffusion = true - # impose bc on both sides of the domain to accomodate a diffusion operator d^2 / d r^2 - @loop_s_z_vperp_vpa is iz ivperp ivpa begin - ir = 1 # r = -L/2 -- check that the point is on lowest rank - if r.irank == 0 && (r_diffusion || adv[is].speed[ir,ivpa,ivperp,iz] > zero) - f[ivpa,ivperp,iz,ir,is] = f_r_bc[ivpa,ivperp,iz,1,is] - end - ir = r.n # r = L/2 -- check that the point is on highest rank - if r.irank == r.nrank - 1 && (r_diffusion || adv[is].speed[ir,ivpa,ivperp,iz] < -zero) - f[ivpa,ivperp,iz,ir,is] = f_r_bc[ivpa,ivperp,iz,end,is] - end - end - end -end - -""" -enforce boundary conditions on charged particle f in z -""" -function enforce_z_boundary_condition!(pdf, density, upar, ppar, moments, bc::String, adv, - z, vperp, vpa, composition, end1::AbstractArray{mk_float,4}, - end2::AbstractArray{mk_float,4}, buffer1::AbstractArray{mk_float,4}, - buffer2::AbstractArray{mk_float,4}) - # this block ensures periodic BC can be supported with distributed memory MPI - if z.nelement_global > z.nelement_local - # reconcile internal element boundaries across processes - # & enforce periodicity and external boundaries if needed - nz = z.n - @loop_s_r_vperp_vpa is ir ivperp ivpa begin - end1[ivpa,ivperp,ir,is] = pdf[ivpa,ivperp,1,ir,is] - end2[ivpa,ivperp,ir,is] = pdf[ivpa,ivperp,nz,ir,is] - end - # check on periodic bc happens inside this call below - @views reconcile_element_boundaries_MPI!(pdf, - end1, end2, buffer1, buffer2, z) - end - # define a zero that accounts for finite precision - zero = 1.0e-14 - # 'constant' BC is time-independent f at upwind boundary - # and constant f beyond boundary - if bc == "constant" - begin_s_r_vperp_vpa_region() - density_offset = 1.0 - vwidth = 1.0 - if z.irank == 0 - @loop_s_r_vperp_vpa is ir ivperp ivpa begin - if adv[is].speed[ivpa,1,ir] > 0.0 - pdf[ivpa,ivperp,1,ir,is] = density_offset * exp(-(vpa.grid[ivpa]^2 + vperp.grid[ivperp]^2)/vwidth^2) / sqrt(pi) - end - end - end - if z.irank == z.nrank - 1 - @loop_s_r_vperp_vpa is ir ivperp ivpa begin - if adv[is].speed[ivpa,end,ir] > 0.0 - pdf[ivpa,ivperp,end,ir,is] = density_offset * exp(-(vpa.grid[ivpa]^2 + vperp.grid[ivperp]^2)/vwidth^2) / sqrt(pi) - end - end - end - # 'periodic' BC enforces periodicity by taking the average of the boundary points - elseif bc == "periodic" && z.nelement_global == z.nelement_local - begin_s_r_vperp_vpa_region() - @loop_s_r_vperp_vpa is ir ivperp ivpa begin - pdf[ivpa,ivperp,1,ir,is] = 0.5*(pdf[ivpa,ivperp,z.n,ir,is]+pdf[ivpa,ivperp,1,ir,is]) - pdf[ivpa,ivperp,z.n,ir,is] = pdf[ivpa,ivperp,1,ir,is] - end - # 'wall' BC enforces wall boundary conditions - elseif bc == "wall" - # Need integrals over vpa at wall boundaries in z, so cannot parallelize over z - # or vpa. - begin_s_r_region() - @loop_s is begin - # zero incoming BC for ions, as they recombine at the wall - if moments.evolve_upar - @loop_r ir begin - @views enforce_zero_incoming_bc!( - pdf[:,:,:,ir,is], z, vpa, density[:,ir,is], upar[:,ir,is], - ppar[:,ir,is], moments.evolve_upar, moments.evolve_ppar, zero) - end - else - @loop_r ir begin - @views enforce_zero_incoming_bc!(pdf[:,:,:,ir,is], - adv[is].speed[:,:,:,ir], z, zero) - end - end - end - end -end - -""" -enforce boundary conditions on neutral particle distribution function -""" -function enforce_neutral_boundary_conditions!(f_neutral, f_charged, - boundary_distributions, density_neutral, uz_neutral, pz_neutral, moments, - density_ion, upar_ion, Er, vzeta_spectral, vr_spectral, vz_spectral, r_adv, z_adv, - vzeta_adv, vr_adv, vz_adv, r, z, vzeta, vr, vz, composition, geometry, - scratch_dummy, r_diffusion, vz_diffusion) - - # without acceleration of neutrals bc on vz vr vzeta should not be required as no - # advection or diffusion in these coordinates - - if vzeta_adv !== nothing && vzeta.n_global > 1 && vzeta.bc != "none" - begin_sn_r_z_vr_vz_region() - @loop_sn_r_z_vr_vz isn ir iz ivr ivz begin - # enforce the vz BC - @views enforce_v_boundary_condition_local!(f_neutral[ivz,ivr,:,iz,ir,isn], - vzeta.bc, - vzeta_adv[isn].speed[ivz,ivr,:,iz,ir], - false, vzeta, vzeta_spectral) - end - end - if vr_adv !== nothing && vr.n_global > 1 && vr.bc != "none" - begin_sn_r_z_vzeta_vz_region() - @loop_sn_r_z_vzeta_vz isn ir iz ivzeta ivz begin - # enforce the vz BC - @views enforce_v_boundary_condition_local!(f_neutral[ivz,:,ivzeta,iz,ir,isn], - vr.bc, - vr_adv[isn].speed[ivz,:,ivzeta,iz,ir], - false, vr, vr_spectral) - end - end - if vz_adv !== nothing && vz.n_global > 1 && vz.bc != "none" - begin_sn_r_z_vzeta_vr_region() - @loop_sn_r_z_vzeta_vr isn ir iz ivzeta ivr begin - # enforce the vz BC - @views enforce_v_boundary_condition_local!(f_neutral[:,ivr,ivzeta,iz,ir,isn], - vz.bc, - vz_adv[isn].speed[:,ivr,ivzeta,iz,ir], - vz_diffusion, vz, vz_spectral) - end - end - # f_initial contains the initial condition for enforcing a fixed-boundary-value condition - if z.n > 1 - begin_sn_r_vzeta_vr_vz_region() - @views enforce_neutral_z_boundary_condition!(f_neutral, density_neutral, uz_neutral, - pz_neutral, moments, density_ion, upar_ion, Er, boundary_distributions, - z_adv, z, vzeta, vr, vz, composition, geometry, - scratch_dummy.buffer_vzvrvzetarsn_1, scratch_dummy.buffer_vzvrvzetarsn_2, - scratch_dummy.buffer_vzvrvzetarsn_3, scratch_dummy.buffer_vzvrvzetarsn_4) - end - if r.n > 1 - begin_sn_z_vzeta_vr_vz_region() - @views enforce_neutral_r_boundary_condition!(f_neutral, boundary_distributions.pdf_rboundary_neutral, - r_adv, vz, vr, vzeta, z, r, composition, - scratch_dummy.buffer_vzvrvzetazsn_1, scratch_dummy.buffer_vzvrvzetazsn_2, - scratch_dummy.buffer_vzvrvzetazsn_3, scratch_dummy.buffer_vzvrvzetazsn_4, - r_diffusion) - end -end - -function enforce_neutral_r_boundary_condition!(f::AbstractArray{mk_float,6}, - f_r_bc::AbstractArray{mk_float,6}, adv, vz, vr, vzeta, z, r, composition, - end1::AbstractArray{mk_float,5}, end2::AbstractArray{mk_float,5}, - buffer1::AbstractArray{mk_float,5}, buffer2::AbstractArray{mk_float,5}, - r_diffusion) #f_initial, - - bc = r.bc - nr = r.n - - if r.nelement_global > r.nelement_local - # reconcile internal element boundaries across processes - # & enforce periodicity and external boundaries if needed - @loop_sn_z_vzeta_vr_vz isn iz ivzeta ivr ivz begin - end1[ivz,ivr,ivzeta,iz,isn] = f[ivz,ivr,ivzeta,iz,1,isn] - end2[ivz,ivr,ivzeta,iz,isn] = f[ivz,ivr,ivzeta,iz,nr,isn] - end - @views reconcile_element_boundaries_MPI!(f, - end1, end2, buffer1, buffer2, r) - end - # 'periodic' BC enforces periodicity by taking the average of the boundary points - # local case only when no communication required - if bc == "periodic" && r.nelement_global == r.nelement_local - @loop_sn_z_vzeta_vr_vz isn iz ivzeta ivr ivz begin - f[ivz,ivr,ivzeta,iz,1,isn] = 0.5*(f[ivz,ivr,ivzeta,iz,1,isn]+f[ivz,ivr,ivzeta,iz,nr,isn]) - f[ivz,ivr,ivzeta,iz,nr,isn] = f[ivz,ivr,ivzeta,iz,1,isn] - end - end - # Dirichlet boundary condition for external endpoints - if bc == "Dirichlet" - zero = 1.0e-10 - # use the old distribution to force the new distribution to have - # consistant-in-time values at the boundary - # impose bc for incoming parts of velocity space only (Hyperbolic PDE) - @loop_sn_z_vzeta_vr_vz isn iz ivzeta ivr ivz begin - ir = 1 # r = -L/2 - # incoming particles and on lowest rank - if r.irank == 0 && (r_diffusion || adv[isn].speed[ir,ivz,ivr,ivzeta,iz] > zero) - f[ivz,ivr,ivzeta,iz,ir,isn] = f_r_bc[ivz,ivr,ivzeta,iz,1,isn] - end - ir = nr # r = L/2 - # incoming particles and on highest rank - if r.irank == r.nrank - 1 && (r_diffusion || adv[isn].speed[ir,ivz,ivr,ivzeta,iz] < -zero) - f[ivz,ivr,ivzeta,iz,ir,isn] = f_r_bc[ivz,ivr,ivzeta,iz,end,isn] - end - end - end -end - -""" -enforce boundary conditions on neutral particle f in z -""" -function enforce_neutral_z_boundary_condition!(pdf, density, uz, pz, moments, density_ion, - upar_ion, Er, boundary_distributions, adv, - z, vzeta, vr, vz, composition, geometry, - end1::AbstractArray{mk_float,5}, end2::AbstractArray{mk_float,5}, - buffer1::AbstractArray{mk_float,5}, buffer2::AbstractArray{mk_float,5}) - - - if z.nelement_global > z.nelement_local - # reconcile internal element boundaries across processes - # & enforce periodicity and external boundaries if needed - nz = z.n - @loop_sn_r_vzeta_vr_vz isn ir ivzeta ivr ivz begin - end1[ivz,ivr,ivzeta,ir,isn] = pdf[ivz,ivr,ivzeta,1,ir,isn] - end2[ivz,ivr,ivzeta,ir,isn] = pdf[ivz,ivr,ivzeta,nz,ir,isn] - end - # check on periodic bc occurs within this call below - @views reconcile_element_boundaries_MPI!(pdf, - end1, end2, buffer1, buffer2, z) - end - - zero = 1.0e-14 - # 'constant' BC is time-independent f at upwind boundary - # and constant f beyond boundary - if z.bc == "constant" - begin_sn_r_vzeta_vr_vz_region() - density_offset = 1.0 - vwidth = 1.0 - if z.irank == 0 - @loop_sn_r_vzeta_vr_vz isn ir ivzeta ivr ivz begin - if adv[isn].speed[ivz,ivr,ivzeta,1,ir] > 0.0 - pdf[ivz,ivr,ivzeta,1,ir,is] = density_offset * - exp(-(vzeta.grid[ivzeta]^2 + vr.grid[ivr] + vz.grid[ivz])/vwidth^2) / - sqrt(pi) - end - end - end - if z.irank == z.nrank - 1 - @loop_sn_r_vzeta_vr_vz isn ir ivzeta ivr ivz begin - if adv[isn].speed[ivz,ivr,ivzeta,end,ir] > 0.0 - pdf[ivz,ivr,ivzeta,end,ir,is] = density_offset * - exp(-(vzeta.grid[ivzeta]^2 + vr.grid[ivr] + vz.grid[ivz])/vwidth^2) / - sqrt(pi) - end - end - end - # 'periodic' BC enforces periodicity by taking the average of the boundary points - elseif z.bc == "periodic" && z.nelement_global == z.nelement_local - begin_sn_r_vzeta_vr_vz_region() - @loop_sn_r_vzeta_vr_vz isn ir ivzeta ivr ivz begin - pdf[ivz,ivr,ivzeta,1,ir,isn] = 0.5*(pdf[ivz,ivr,ivzeta,1,ir,isn] + - pdf[ivz,ivr,ivzeta,end,ir,isn]) - pdf[ivz,ivr,ivzeta,end,ir,isn] = pdf[ivz,ivr,ivzeta,1,ir,isn] - end - # 'wall' BC enforces wall boundary conditions - elseif z.bc == "wall" - # Need integrals over vpa at wall boundaries in z, so cannot parallelize over z - # or vpa. - begin_sn_r_region() - @loop_sn isn begin - # BC for neutrals - @loop_r ir begin - # define vtfac to avoid repeated computation below - vtfac = sqrt(composition.T_wall * composition.mn_over_mi) - # Assume for now that the ion species index corresponding to this neutral - # species is the same as the neutral species index. - # Note, have already calculated moments of ion distribution function(s), - # so can use the moments here to get the flux - if z.irank == 0 - ion_flux_0 = -density_ion[1,ir,isn] * (upar_ion[1,ir,isn]*geometry.bzed[1,ir] - 0.5*geometry.rhostar*Er[1,ir]) - else - ion_flux_0 = NaN - end - if z.irank == z.nrank - 1 - ion_flux_L = density_ion[end,ir,isn] * (upar_ion[end,ir,isn]*geometry.bzed[end,ir] - 0.5*geometry.rhostar*Er[end,ir]) - else - ion_flux_L = NaN - end - # enforce boundary condition on the neutral pdf that all ions and neutrals - # that leave the domain re-enter as neutrals - @views enforce_neutral_wall_bc!( - pdf[:,:,:,:,ir,isn], z, vzeta, vr, vz, pz[:,ir,isn], uz[:,ir,isn], - density[:,ir,isn], ion_flux_0, ion_flux_L, boundary_distributions, - vtfac, composition.recycling_fraction, moments.evolve_ppar, - moments.evolve_upar, moments.evolve_density, zero) - end - end - end -end """ -enforce a zero incoming BC in z for given species pdf at each radial location -""" -function enforce_zero_incoming_bc!(pdf, speed, z, zero) - nvpa = size(pdf,1) - # no parallel BC should be enforced for dz/dt = 0 - # note that the parallel velocity coordinate vpa may be dz/dt or - # some version of the peculiar velocity (dz/dt - upar), - # so use advection speed below instead of vpa - if z.irank == 0 - @loop_vperp_vpa ivperp ivpa begin - # for left boundary in zed (z = -Lz/2), want - # f(z=-Lz/2, v_parallel > 0) = 0 - if speed[1,ivpa,ivperp] > zero - pdf[ivpa,ivperp,1] = 0.0 - end - end - end - if z.irank == z.nrank - 1 - @loop_vperp_vpa ivperp ivpa begin - # for right boundary in zed (z = Lz/2), want - # f(z=Lz/2, v_parallel < 0) = 0 - if speed[end,ivpa,ivperp] < -zero - pdf[ivpa,ivperp,end] = 0.0 - end - end - end -end -function enforce_zero_incoming_bc!(pdf, z::coordinate, vpa::coordinate, density, upar, - ppar, evolve_upar, evolve_ppar, zero) - if z.irank != 0 && z.irank != z.nrank - 1 - # No z-boundary in this block - return nothing - end - nvpa, nvperp, nz = size(pdf) - # no parallel BC should be enforced for dz/dt = 0 - # note that the parallel velocity coordinate vpa may be dz/dt or - # some version of the peculiar velocity (dz/dt - upar), - # so use advection speed below instead of vpa - - # absolute velocity at left boundary - if z.irank == 0 - @. vpa.scratch = vpagrid_to_dzdt(vpa.grid, sqrt(2.0*(ppar[1]/density[1])), - upar[1], evolve_ppar, evolve_upar) - @loop_vpa ivpa begin - # for left boundary in zed (z = -Lz/2), want - # f(z=-Lz/2, v_parallel > 0) = 0 - if vpa.scratch[ivpa] > zero - pdf[ivpa,:,1] .= 0.0 - end - end - end - # absolute velocity at right boundary - if z.irank == z.nrank - 1 - @. vpa.scratch2 = vpagrid_to_dzdt(vpa.grid, sqrt(2.0*(ppar[end]/density[end])), - upar[end], evolve_ppar, evolve_upar) - @loop_vpa ivpa begin - # for right boundary in zed (z = Lz/2), want - # f(z=Lz/2, v_parallel < 0) = 0 - if vpa.scratch2[ivpa] < -zero - pdf[ivpa,:,end] .= 0.0 - end - end - end - - # Special constraint-forcing code that tries to keep the modifications smooth at - # v_parallel=0. - if z.irank == 0 && z.irank == z.nrank - 1 - # Both z-boundaries in this block - z_range = (1,nz) - elseif z.irank == 0 - z_range = (1,) - elseif z.irank == z.nrank - 1 - z_range = (nz,) - else - error("No boundary in this block, should have returned already") - end - for iz ∈ z_range - # moment-kinetic approach only implemented for 1V case so far - @boundscheck size(pdf,2) == 1 - - f = @view pdf[:,1,iz] - if evolve_ppar && evolve_upar - I0 = integrate_over_vspace(f, vpa.wgts) - I1 = integrate_over_vspace(f, vpa.grid, vpa.wgts) - I2 = integrate_over_vspace(f, vpa.grid, 2, vpa.wgts) - - # Store v_parallel with upar shift removed in vpa.scratch - vth = sqrt(2.0*ppar[iz]/density[iz]) - @. vpa.scratch = vpa.grid + upar[iz]/vth - # Introduce factor to ensure corrections go smoothly to zero near - # v_parallel=0 - @. vpa.scratch2 = f * abs(vpa.scratch) / (1.0 + abs(vpa.scratch)) - J1 = integrate_over_vspace(vpa.scratch2, vpa.grid, vpa.wgts) - J2 = integrate_over_vspace(vpa.scratch2, vpa.grid, 2, vpa.wgts) - J3 = integrate_over_vspace(vpa.scratch2, vpa.grid, 3, vpa.wgts) - J4 = integrate_over_vspace(vpa.scratch2, vpa.grid, 4, vpa.wgts) - - A = (J3^2 - J2*J4 + 0.5*(J2^2 - J1*J3)) / - (I0*(J3^2 - J2*J4) + I1*(J1*J4 - J2*J3) + I2*(J2^2 - J1*J3)) - B = (0.5*J3 + A*(I1*J4 - I2*J3)) / (J3^2 - J2*J4) - C = (0.5 - A*I2 -B*J3) / J4 - - @. f = A*f + B*vpa.grid*vpa.scratch2 + C*vpa.grid*vpa.grid*vpa.scratch2 - elseif evolve_upar - I0 = integrate_over_vspace(f, vpa.wgts) - I1 = integrate_over_vspace(f, vpa.grid, vpa.wgts) - - # Store v_parallel with upar shift removed in vpa.scratch - @. vpa.scratch = vpa.grid + upar[iz] - # Introduce factor to ensure corrections go smoothly to zero near - # v_parallel=0 - @. vpa.scratch2 = f * abs(vpa.scratch) / (1.0 + abs(vpa.scratch)) - J1 = integrate_over_vspace(vpa.scratch2, vpa.grid, vpa.wgts) - J2 = integrate_over_vspace(vpa.scratch2, vpa.grid, 2, vpa.wgts) - - A = 1.0 / (I0 - I1*J1/J2) - B = -A*I1/J2 - - @. f = A*f + B*vpa.grid*vpa.scratch2 - elseif evolve_density - I0 = integrate_over_vspace(f, vpa.wgts) - @. f = f / I0 - end - end -end - -""" -Set up an initial condition that tries to be smoothly compatible with the sheath -boundary condition for ions, by setting f(±(v_parallel-u0)<0) where u0=0 at the sheath -boundaries and for z<0 increases linearly to u0=vpa.L at z=0, while for z>0 increases -from u0=-vpa.L at z=0 to zero at the z=z.L/2 sheath. - -To be applied to 'full-f' distribution function on v_parallel grid (not w_parallel -grid). -""" -function enforce_initial_tapered_zero_incoming!(pdf, z::coordinate, vpa::coordinate) - nvpa = size(pdf,1) - zero = 1.0e-14 - # no parallel BC should be enforced for dz/dt = 0 - # note that the parallel velocity coordinate vpa may be dz/dt or - # some version of the peculiar velocity (dz/dt - upar), - # so use advection speed below instead of vpa - - for iz ∈ 1:z.n - u0 = (2.0*z.grid[iz]/z.L - sign(z.grid[iz])) * vpa.L / 2.0 - if z.grid[iz] < -zero - for ivpa ∈ 1:nvpa - if vpa.grid[ivpa] > u0 + zero - pdf[ivpa,iz] = 0.0 - end - end - elseif z.grid[iz] > zero - for ivpa ∈ 1:nvpa - if vpa.grid[ivpa] < u0 - zero - pdf[ivpa,iz] = 0.0 - end - end - end - end -end - -""" -enforce the wall boundary condition on neutrals; -i.e., the incoming flux of neutrals equals the sum of the ion/neutral outgoing fluxes -""" -function enforce_neutral_wall_bc!(pdf, z, vzeta, vr, vz, pz, uz, density, wall_flux_0, - wall_flux_L, boundary_distributions, vtfac, - recycling_fraction, evolve_ppar, evolve_upar, - evolve_density, zero) - - # Reduce the ion flux by `recycling_fraction` to account for ions absorbed by the - # wall. - wall_flux_0 *= recycling_fraction - wall_flux_L *= recycling_fraction - - if !evolve_density && !evolve_upar - knudsen_cosine = boundary_distributions.knudsen - - if z.irank == 0 - ## treat z = -Lz/2 boundary ## - - # add the neutral species's contribution to the combined ion/neutral particle - # flux out of the domain at z=-Lz/2 - @views wall_flux_0 += integrate_over_negative_vz(abs.(vz.grid) .* pdf[:,:,:,1], vz.grid, vz.wgts, vz.scratch, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) - - # for left boundary in zed (z = -Lz/2), want - # f_n(z=-Lz/2, v_parallel > 0) = Γ_0 * f_KW(v_parallel) - @loop_vz ivz begin - if vz.grid[ivz] >= -zero - @views @. pdf[ivz,:,:,1] = wall_flux_0 * knudsen_cosine[ivz,:,:] - end - end - end - - if z.irank == z.nrank - 1 - ## treat the right boundary at z = Lz/2 ## - - # add the neutral species's contribution to the combined ion/neutral particle - # flux out of the domain at z=-Lz/2 - @views wall_flux_L += integrate_over_positive_vz(abs.(vz.grid) .* pdf[:,:,:,end], vz.grid, vz.wgts, vz.scratch, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) - - # for right boundary in zed (z = Lz/2), want - # f_n(z=Lz/2, v_parallel < 0) = Γ_Lz * f_KW(v_parallel) - @loop_vz ivz begin - if vz.grid[ivz] <= zero - @views @. pdf[ivz,:,:,end] = wall_flux_L * knudsen_cosine[ivz,:,:] - end - end - end - elseif !evolve_upar - # Evolving density case - knudsen_cosine = boundary_distributions.knudsen - - if z.irank == 0 - ## treat z = -Lz/2 boundary ## - - # Note the numerical integrol of knudsen_cosine was forced to be 1 (to machine - # precision) when it was initialised. - @views pdf_integral_0 = integrate_over_negative_vz(pdf[:,:,:,1], vz.grid, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) - @views pdf_integral_1 = integrate_over_negative_vz(vz.grid .* pdf[:,:,:,1], vz.grid, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) - knudsen_integral_0 = integrate_over_positive_vz(knudsen_cosine, vz.grid, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) - knudsen_integral_1 = 1.0 # This is enforced in initialization - - # Calculate normalisation factors N_in for the incoming and N_out for the - # Knudsen parts of the distirbution so that ∫dvpa F = 1 and ∫dvpa vpa F = uz - # Note wall_flux_0 is the ion flux into the wall (reduced by the recycling - # fraction), and the neutral flux should be out of the wall (i.e. uz>0) so - # n*uz = |n*uz| = wall_flux_0 - # ⇒ N_in*pdf_integral_0 + N_out*knudsen_integral_0 = 1 - # N_in*pdf_integral_1 + N_out*knudsen_integral_1 = uz - uz = wall_flux_0 / density[1] - N_in = (1 - uz * knudsen_integral_0 / knudsen_integral_1) / - (pdf_integral_0 - - pdf_integral_1 / knudsen_integral_1 * knudsen_integral_0) - N_out = (uz - N_in * pdf_integral_1) / knudsen_integral_1 - - @loop_vz ivz begin - if vz.grid[ivz] >= -zero - @views @. pdf[ivz,:,:,1] = N_out * knudsen_cosine[ivz,:,:] - else - @views @. pdf[ivz,:,:,1] *= N_in - end - end - end - - if z.irank == z.nrank - 1 - ## treat the right boundary at z = Lz/2 ## - - # Note the numerical integrol of knudsen_cosine was forced to be 1 (to machine - # precision) when it was initialised. - @views pdf_integral_0 = integrate_over_positive_vz(pdf[:,:,:,end], vz.grid, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) - @views pdf_integral_1 = integrate_over_positive_vz(vz.grid .* pdf[:,:,:,end], vz.grid, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) - knudsen_integral_0 = integrate_over_negative_vz(knudsen_cosine, vz.grid, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) - knudsen_integral_1 = -1.0 # This is enforced in initialization - - # Calculate normalisation factors N_in for the incoming and N_out for the - # Knudsen parts of the distirbution so that ∫dvpa F = 1 and ∫dvpa vpa F = uz - # Note wall_flux_L is the ion flux into the wall (reduced by the recycling - # fraction), and the neutral flux should be out of the wall (i.e. uz<0) so - # -n*uz = |n*uz| = wall_flux_L - # ⇒ N_in*pdf_integral_0 + N_out*knudsen_integral_0 = 1 - # N_in*pdf_integral_1 + N_out*knudsen_integral_1 = uz - uz = -wall_flux_L / density[end] - N_in = (1 - uz * knudsen_integral_0 / knudsen_integral_1) / - (pdf_integral_0 - - pdf_integral_1 / knudsen_integral_1 * knudsen_integral_0) - N_out = (uz - N_in * pdf_integral_1) / knudsen_integral_1 - - @loop_vz ivz begin - if vz.grid[ivz] <= zero - @views @. pdf[ivz,:,:,end] = N_out * knudsen_cosine[ivz,:,:] - else - @views @. pdf[ivz,:,:,end] *= N_in - end - end - end - else - if z.irank == 0 - ## treat z = -Lz/2 boundary ## - # populate vz.scratch2 array with dz/dt values at z = -Lz/2 - if evolve_ppar - vth = sqrt(2.0*pz[1]/density[1]) - else - vth = nothing - end - @. vz.scratch2 = vpagrid_to_dzdt(vz.grid, vth, uz[1], evolve_ppar, evolve_upar) - - # First apply boundary condition that total neutral outflux is equal to ion - # influx to uz - uz[1] = wall_flux_0 / density[1] - #would setting density work better?? - #density[1] = - wall_flux_0 / uz[1] - - # Create normalised Knudsen cosine distribution, to use for positive v_parallel - # at z = -Lz/2 - # Note this only makes sense for the 1V case with vr.n=vzeta.n=1 - @. vz.scratch = (3.0*pi/vtfac^3)*abs(vz.scratch2)*erfc(abs(vz.scratch2)/vtfac) - - # The v_parallel>0 part of the pdf is replaced by the Knudsen cosine - # distribution. To ensure the constraints ∫dwpa wpa^m F = 0 are satisfied when - # necessary, calculate a normalisation factor for the Knudsen distribution (in - # vz.scratch) and correction terms for the incoming pdf similar to - # enforce_moment_constraints!(). - # - # Note that it seems to be important that this boundary condition not be - # modified by the moment constraints, as that could cause numerical instability. - # By ensuring that the constraints are satisfied already here, - # enforce_moment_constraints!() will not change the pdf at the boundary. For - # ions this is not an issue, because points set to 0 by the bc are not modified - # from 0 by enforce_moment_constraints!(). - knudsen_integral_0 = integrate_over_positive_vz(vz.scratch, vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) - knudsen_integral_1 = integrate_over_positive_vz(vz.grid .* vz.scratch, vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) - - @views pdf_integral_0 = integrate_over_negative_vz(pdf[:,:,:,1], vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) - @views pdf_integral_1 = integrate_over_negative_vz(vz.grid .* pdf[:,:,:,1], vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) - if !evolve_ppar - # Calculate normalisation factors N_in for the incoming and N_out for the - # Knudsen parts of the distirbution so that ∫dwpa F = 1 and ∫dwpa wpa F = 0 - # ⇒ N_in*pdf_integral_0 + N_out*knudsen_integral_0 = 1 - # N_in*pdf_integral_1 + N_out*knudsen_integral_1 = 0 - N_in = 1.0 / (pdf_integral_0 - pdf_integral_1/knudsen_integral_1*knudsen_integral_0) - N_out = -N_in * pdf_integral_1 / knudsen_integral_1 - - zero_vz_ind = 0 - for ivz ∈ 1:vz.n - if vz.scratch2[ivz] <= -zero - pdf[ivz,:,:,1] .= N_in*pdf[ivz,:,:,1] - else - zero_vz_ind = ivz - if abs(vz.scratch2[ivz]) < zero - # v_z = 0 point, half contribution from original pdf and half - # from Knudsen cosine distribution, to be consistent with weights - # used in - # integrate_over_positive_vz()/integrate_over_negative_vz(). - @. pdf[ivz,:,:,1] = 0.5*(N_in*pdf[ivz,:,:,1] + N_out*vz.scratch[ivz]) - else - pdf[ivz,:,:,1] .= N_out*vz.scratch[ivz] - end - break - end - end - for ivz ∈ zero_vz_ind+1:vz.n - pdf[ivz,:,:,1] .= N_out*vz.scratch[ivz] - end - else - knudsen_integral_2 = integrate_over_positive_vz(vz.grid .* vz.grid .* vz.scratch, vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) - @views pdf_integral_2 = integrate_over_negative_vz(vz.grid .* vz.grid .* pdf[:,:,:,1], vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) - @views pdf_integral_3 = integrate_over_negative_vz(vz.grid .* vz.grid .* vz.grid .* pdf[:,:,:,1], vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) - # Calculate normalisation factor N_out for the Knudsen part of the - # distirbution and normalisation factor N_in and correction term C*wpa*F_in - # for the incoming distribution so that ∫dwpa F = 1, ∫dwpa wpa F = 0, and - # ∫dwpa wpa^2 F = 1/2 - # ⇒ N_in*pdf_integral_0 + C*pdf_integral_1 + N_out*knudsen_integral_0 = 1 - # N_in*pdf_integral_1 + C*pdf_integral_2 + N_out*knudsen_integral_1 = 0 - # N_in*pdf_integral_2 + C*pdf_integral_3 + N_out*knudsen_integral_2 = 1/2 - N_in = (0.5*knudsen_integral_0*pdf_integral_2 + - knudsen_integral_1*(pdf_integral_3 - 0.5*pdf_integral_1) - - knudsen_integral_2*pdf_integral_2) / - (knudsen_integral_0*(pdf_integral_2^2 - pdf_integral_1*pdf_integral_3) + - knudsen_integral_1*(pdf_integral_0*pdf_integral_3 - pdf_integral_1*pdf_integral_2) + - knudsen_integral_2*(pdf_integral_1^2 - pdf_integral_0*pdf_integral_2)) - N_out = -(N_in*(pdf_integral_1*pdf_integral_3 - pdf_integral_2^2) + 0.5*pdf_integral_2) / - (knudsen_integral_1*pdf_integral_3 - knudsen_integral_2*pdf_integral_2) - C = (0.5 - N_out*knudsen_integral_2 - N_in*pdf_integral_2)/pdf_integral_3 - - zero_vz_ind = 0 - for ivz ∈ 1:vz.n - if vz.scratch2[ivz] <= -zero - @views @. pdf[ivz,:,:,1] = N_in*pdf[ivz,:,:,1] + C*vz.grid[ivz]*pdf[ivz,:,:,1] - else - zero_vz_ind = ivz - if abs(vz.scratch2[ivz]) < zero - # v_parallel = 0 point, half contribution from original pdf and half - # from Knudsen cosine distribution, to be consistent with weights - # used in - # integrate_over_positive_vz()/integrate_over_negative_vz(). - @views @. pdf[ivz,:,:,1] = 0.5*(N_in*pdf[ivz,:,:,1] + - C*vz.grid[ivz]*pdf[ivz,:,:,1] + - N_out*vz.scratch[ivz]) - else - @. pdf[ivz,:,:,1] = N_out*vz.scratch[ivz] - end - break - end - end - for ivz ∈ zero_vz_ind+1:vz.n - @. pdf[ivz,:,:,1] = N_out*vz.scratch[ivz] - end - end - end - - if z.irank == z.nrank - 1 - ## treat the right boundary at z = Lz/2 ## - # populate vz.scratch2 array with dz/dt values at z = Lz/2 - if evolve_ppar - vth = sqrt(2.0*pz[end]/density[end]) - else - vth = nothing - end - @. vz.scratch2 = vpagrid_to_dzdt(vz.grid, vth, uz[end], evolve_ppar, evolve_upar) - - # First apply boundary condition that total neutral outflux is equal to ion - # influx to uz - uz[end] = - wall_flux_L / density[end] - #would setting density work better?? - #density[end] = - wall_flux_L / upar[end] - - # obtain the Knudsen cosine distribution at z = Lz/2 - # the z-dependence is only introduced if the peculiiar velocity is used as vz - # Note this only makes sense for the 1V case with vr.n=vzeta.n=1 - @. vz.scratch = (3.0*pi/vtfac^3)*abs(vz.scratch2)*erfc(abs(vz.scratch2)/vtfac) - - # The v_parallel<0 part of the pdf is replaced by the Knudsen cosine - # distribution. To ensure the constraint ∫dwpa wpa F = 0 is satisfied, multiply - # the Knudsen distribution (in vz.scratch) by a normalisation factor given by - # the integral (over negative v_parallel) of the outgoing Knudsen distribution - # and (over positive v_parallel) of the incoming pdf. - knudsen_integral_0 = integrate_over_negative_vz(vz.scratch, vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) - knudsen_integral_1 = integrate_over_negative_vz(vz.grid .* vz.scratch, vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) - - @views pdf_integral_0 = integrate_over_positive_vz(pdf[:,:,:,end], vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) - @views pdf_integral_1 = integrate_over_positive_vz(vz.grid .* pdf[:,:,:,end], vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) - - if !evolve_ppar - # Calculate normalisation factors N_in for the incoming and N_out for the - # Knudsen parts of the distirbution so that ∫dwpa F = 1 and ∫dwpa wpa F = 0 - # ⇒ N_in*pdf_integral_0 + N_out*knudsen_integral_0 = 1 - # N_in*pdf_integral_1 + N_out*knudsen_integral_1 = 0 - N_in = 1.0 / (pdf_integral_0 - pdf_integral_1/knudsen_integral_1*knudsen_integral_0) - N_out = -N_in * pdf_integral_1 / knudsen_integral_1 - - zero_vz_ind = 0 - for ivz ∈ vz.n:-1:1 - if vz.scratch2[ivz] >= zero - @views @. pdf[ivz,:,:,end] = N_in*pdf[ivz,:,:,end] - else - zero_vz_ind = ivz - if abs(vz.scratch2[ivz]) < zero - # v_parallel = 0 point, half contribution from original pdf and half - # from Knudsen cosine distribution, to be consistent with weights - # used in - # integrate_over_positive_vz()/integrate_over_negative_vz(). - @views @. pdf[ivz,:,:,end] = 0.5*(N_in*pdf[ivz,:,:,end] + N_out*vz.scratch[ivz]) - else - @. pdf[ivz,:,:,end] = N_out*vz.scratch[ivz] - end - break - end - end - for ivz ∈ 1:zero_vz_ind-1 - @. pdf[ivz,:,:,end] = N_out*vz.scratch[ivz] - end - else - knudsen_integral_2 = integrate_over_negative_vz(vz.grid .* vz.grid .* vz.scratch, vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) - @views pdf_integral_2 = integrate_over_positive_vz(vz.grid .* vz.grid .* pdf[:,:,:,end], vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) - @views pdf_integral_3 = integrate_over_positive_vz(vz.grid .* vz.grid .* vz.grid .* pdf[:,:,:,end], vz.scratch2, vz.wgts, vz.scratch3, vr.grid, vr.wgts, vzeta.grid, vzeta.wgts) - # Calculate normalisation factor N_out for the Knudsen part of the - # distirbution and normalisation factor N_in and correction term C*wpa*F_in - # for the incoming distribution so that ∫dwpa F = 1, ∫dwpa wpa F = 0, and - # ∫dwpa wpa^2 F = 1/2 - # ⇒ N_in*pdf_integral_0 + C*pdf_integral_1 + N_out*knudsen_integral_0 = 1 - # N_in*pdf_integral_1 + C*pdf_integral_2 + N_out*knudsen_integral_1 = 0 - # N_in*pdf_integral_2 + C*pdf_integral_3 + N_out*knudsen_integral_2 = 1/2 - N_in = (0.5*knudsen_integral_0*pdf_integral_2 + - knudsen_integral_1*(pdf_integral_3 - 0.5*pdf_integral_1) - - knudsen_integral_2*pdf_integral_2) / - (knudsen_integral_0*(pdf_integral_2^2 - pdf_integral_1*pdf_integral_3) + - knudsen_integral_1*(pdf_integral_0*pdf_integral_3 - pdf_integral_1*pdf_integral_2) + - knudsen_integral_2*(pdf_integral_1^2 - pdf_integral_0*pdf_integral_2)) - N_out = -(N_in*(pdf_integral_1*pdf_integral_3 - pdf_integral_2^2) + 0.5*pdf_integral_2) / - (knudsen_integral_1*pdf_integral_3 - knudsen_integral_2*pdf_integral_2) - C = (0.5 - N_out*knudsen_integral_2 - N_in*pdf_integral_2)/pdf_integral_3 - - zero_vz_ind = 0 - for ivz ∈ vz.n:-1:1 - if vz.scratch2[ivz] >= zero - @views @. pdf[ivz,:,:,end] = N_in*pdf[ivz,:,:,end] + C*vz.grid[ivz]*pdf[ivz,:,:,end] - else - zero_vz_ind = ivz - if abs(vz.scratch2[ivz]) < zero - # v_parallel = 0 point, half contribution from original pdf and half - # from Knudsen cosine distribution, to be consistent with weights - # used in - # integrate_over_positive_vz()/integrate_over_negative_vz(). - @views @. pdf[ivz,:,:,end] = 0.5*(N_in*pdf[ivz,:,:,end] + - C*vz.grid[ivz]*pdf[ivz,:,:,end] + - N_out*vz.scratch[ivz]) - else - @. pdf[ivz,:,:,end] = N_out*vz.scratch[ivz] - end - break - end - end - for ivz ∈ 1:zero_vz_ind-1 - @. pdf[ivz,:,:,end] = N_out*vz.scratch[ivz] - end - end - end - end -end - -""" -create an array of dz/dt values corresponding to the given vpagrid values -""" -function vpagrid_to_dzdt(vpagrid, vth, upar, evolve_ppar, evolve_upar) - if evolve_ppar - if evolve_upar - return vpagrid .* vth .+ upar - else - return vpagrid .* vth - end - elseif evolve_upar - return vpagrid .+ upar - else - return vpagrid - end -end - -""" -Take the full charged-particle distribution function, calculate the moments, then +Take the full ion distribution function, calculate the moments, then normalise and shift to the moment-kinetic grid. Uses input value of `f` and modifies in place to the normalised distribution functions. @@ -2057,7 +1139,7 @@ the moments of `f`. Inputs/outputs depend on z, vperp, and vpa (should be inside loops over species, r) """ -function convert_full_f_charged_to_normalised!(f, density, upar, ppar, vth, vperp, vpa, +function convert_full_f_ion_to_normalised!(f, density, upar, ppar, vth, vperp, vpa, vpa_spectral, evolve_density, evolve_upar, evolve_ppar) @loop_z iz begin @@ -2146,91 +1228,4 @@ function convert_full_f_neutral_to_normalised!(f, density, uz, pz, vth, vzeta, v return nothing end -""" -enforce the z boundary condition on the evolved velocity space moments of f -""" -function enforce_z_boundary_condition_moments!(density, moments, bc::String) - ## TODO: parallelise - #begin_serial_region() - #@serial_region begin - # # enforce z boundary condition on density if it is evolved separately from f - # if moments.evolve_density - # # TODO: extend to 'periodic' BC case, as this requires further code modifications to be consistent - # # with finite difference derivatives (should be fine for Chebyshev) - # if bc == "wall" - # @loop_s_r is ir begin - # density[1,ir,is] = 0.5*(density[1,ir,is] + density[end,ir,is]) - # density[end,ir,is] = density[1,ir,is] - # end - # end - # end - #end -end - -""" -""" -function enforce_v_boundary_condition_local!(f, bc, speed, v_diffusion, v, v_spectral) - if bc == "zero" - if v_diffusion || speed[1] > 0.0 - # 'upwind' boundary - f[1] = 0.0 - end - if v_diffusion || speed[end] < 0.0 - # 'upwind' boundary - f[end] = 0.0 - end - elseif bc == "both_zero" - f[1] = 0.0 - f[end] = 0.0 - elseif bc == "zero_gradient" - D0 = v_spectral.lobatto.Dmat[1,:] - # adjust F(vpa = -L/2) so that d F / d vpa = 0 at vpa = -L/2 - f[1] = -sum(D0[2:v.ngrid].*f[2:v.ngrid])/D0[1] - - D0 = v_spectral.lobatto.Dmat[end,:] - # adjust F(vpa = L/2) so that d F / d vpa = 0 at vpa = L/2 - f[end] = -sum(D0[1:ngrid-1].*f[end-v.ngrid+1:end-1])/D0[v.ngrid] - elseif bc == "periodic" - f[1] = 0.5*(f[1]+f[end]) - f[end] = f[1] - else - error("Unsupported boundary condition option '$bc' for $(v.name)") - end -end - -""" -enforce zero boundary condition at vperp -> infinity -""" -function enforce_vperp_boundary_condition!(f, bc, vperp, vperp_spectral, vperp_advect, diffusion) - if bc == "zero" - nvperp = vperp.n - ngrid = vperp.ngrid - # set zero boundary condition - @loop_s_r_z_vpa is ir iz ivpa begin - if diffusion || vperp_advect[is].speed[nvperp,ivpa,iz,ir] < 0.0 - f[ivpa,nvperp,iz,ir,is] = 0.0 - end - end - # set regularity condition d F / d vperp = 0 at vperp = 0 - if vperp.discretization == "gausslegendre_pseudospectral" || vperp.discretization == "chebyshev_pseudospectral" - D0 = vperp_spectral.radau.D0 - buffer = @view vperp.scratch[1:ngrid-1] - @loop_s_r_z_vpa is ir iz ivpa begin - if diffusion || vperp_advect[is].speed[1,ivpa,iz,ir] > 0.0 - # adjust F(vperp = 0) so that d F / d vperp = 0 at vperp = 0 - @views @. buffer = D0[2:ngrid] * f[ivpa,2:ngrid,iz,ir,is] - f[ivpa,1,iz,ir,is] = -sum(buffer)/D0[1] - end - end - else - println("vperp.bc=\"$bc\" not supported by discretization " - * "$(vperp.discretization)") - end - elseif bc == "none" - # Do nothing - else - error("Unsupported boundary condition option '$bc' for vperp") - end -end - end diff --git a/moment_kinetics/src/input_structs.jl b/moment_kinetics/src/input_structs.jl index 2b716a172..fe7661e9a 100644 --- a/moment_kinetics/src/input_structs.jl +++ b/moment_kinetics/src/input_structs.jl @@ -2,6 +2,7 @@ """ module input_structs +export advance_info export evolve_moments_options export time_info export advection_input, advection_input_mutable @@ -91,7 +92,7 @@ mutable struct advance_info ionization_collisions::Bool ionization_collisions_1V::Bool ionization_source::Bool - krook_collisions::Bool + krook_collisions_ii::Bool explicit_weakform_fp_collisions::Bool external_source::Bool numerical_dissipation::Bool @@ -348,8 +349,8 @@ end """ Base.@kwdef struct krook_collisions_input use_krook::Bool - # Coulomb collision rate at the reference density and temperature - krook_collision_frequency_prefactor::mk_float# + # Ion-ion Coulomb collision rate at the reference density and temperature + nuii0::mk_float # Setting to switch between different options for Krook collision operator frequency_option::String # "reference_parameters" # "manual", end @@ -425,6 +426,7 @@ Base.@kwdef struct io_input ascii_output::Bool binary_format::binary_format_type parallel_io::Bool + run_id::String end """ diff --git a/moment_kinetics/src/ionization.jl b/moment_kinetics/src/ionization.jl index 91a8d1d4b..a4e7ac3f1 100644 --- a/moment_kinetics/src/ionization.jl +++ b/moment_kinetics/src/ionization.jl @@ -95,7 +95,7 @@ function ionization_collisions_1V!(f_out, f_neutral_out, fvec_in, vz, vpa, vperp if moments.evolve_ppar # will need the ratio of thermal speeds both to interpolate between vpa grids # for different species and to account for different normalizations of each species' pdf - vth_ratio = moments.charged.vth[iz,ir,is]/moments.neutral.vth[iz,ir,isn] + vth_ratio = moments.ion.vth[iz,ir,is]/moments.neutral.vth[iz,ir,isn] else vth_ratio = 1.0 end @@ -129,7 +129,7 @@ function ionization_collisions_1V!(f_out, f_neutral_out, fvec_in, vz, vpa, vperp # to get f_{s'}(wpahat_s), need to obtain wpahat_s grid locations # in terms of the wpahat_{s'} coordinate: # (wpahat_{s'})_j = ((wpahat_{s})_j * vth_{s} + upar_{s} - upar_{s'}) / vth_{s'} - @. vpa.scratch = (vpa.grid * moments.charged.vth[iz,ir,is] + fvec_in.upar[iz,ir,is] - fvec_in.uz_neutral[iz,ir,isn]) / moments.neutral.vth[iz,ir,isn] + @. vpa.scratch = (vpa.grid * moments.ion.vth[iz,ir,is] + fvec_in.upar[iz,ir,is] - fvec_in.uz_neutral[iz,ir,isn]) / moments.neutral.vth[iz,ir,isn] end # interpolate to the new grid (passed in as vpa.scratch) # and return interpolated values in vpa.scratch2 diff --git a/moment_kinetics/src/krook_collisions.jl b/moment_kinetics/src/krook_collisions.jl index d487c195d..dce34db29 100644 --- a/moment_kinetics/src/krook_collisions.jl +++ b/moment_kinetics/src/krook_collisions.jl @@ -2,12 +2,11 @@ """ module krook_collisions -export setup_krook_collisions_input, get_collision_frequency, krook_collisions! +export setup_krook_collisions_input, get_collision_frequency_ii, krook_collisions! -using ..constants: epsilon0, proton_charge using ..looping using ..input_structs: krook_collisions_input, set_defaults_and_check_section! -using ..reference_parameters: get_reference_collision_frequency +using ..reference_parameters: get_reference_collision_frequency_ii """ @@ -21,18 +20,18 @@ frequency_option = "manual" """ function setup_krook_collisions_input(toml_input::Dict, reference_params) # get reference collision frequency - nuii_krook_default = get_reference_collision_frequency(reference_params) + nuii_krook_default = get_reference_collision_frequency_ii(reference_params) # read the input toml and specify a sensible default input_section = input_section = set_defaults_and_check_section!(toml_input, "krook_collisions", # begin default inputs (as kwargs) use_krook = false, - krook_collision_frequency_prefactor = -1.0, + nuii0 = -1.0, frequency_option = "reference_parameters") # ensure that the collision frequency is consistent with the input option frequency_option = input_section["frequency_option"] if frequency_option == "reference_parameters" - input_section["krook_collision_frequency_prefactor"] = nuii_krook_default + input_section["nuii0"] = nuii_krook_default elseif frequency_option == "manual" # use the frequency from the input file # do nothing @@ -43,7 +42,7 @@ function setup_krook_collisions_input(toml_input::Dict, reference_params) # finally, ensure prefactor < 0 if use_krook is false # so that prefactor > 0 is the only check required in the rest of the code if !input_section["use_krook"] - input_section["krook_collision_frequency_prefactor"] = -1.0 + input_section["nuii0"] = -1.0 end input = Dict(Symbol(k)=>v for (k,v) in input_section) #println(input) @@ -51,21 +50,21 @@ function setup_krook_collisions_input(toml_input::Dict, reference_params) end """ - get_collision_frequency(collisions, n, vth) + get_collision_frequency_ii(collisions, n, vth) -Calculate the collision frequency, depending on the settings/parameters in `collisions`, -for the given density `n` and thermal speed `vth`. +Calculate the ion-ion collision frequency, depending on the settings/parameters in +`collisions`, for the given density `n` and thermal speed `vth`. `n` and `vth` may be scalars or arrays, but should have shapes that can be broadcasted together. """ -function get_collision_frequency(collisions, n, vth) +function get_collision_frequency_ii(collisions, n, vth) # extract krook options from collisions struct colk = collisions.krook - krook_collision_frequency_prefactor = colk.krook_collision_frequency_prefactor + nuii0 = colk.nuii0 frequency_option = colk.frequency_option if frequency_option == "reference_parameters" - return @. krook_collision_frequency_prefactor * n * vth^(-3) + return @. nuii0 * n * vth^(-3) elseif frequency_option == "manual" # Include 0.0*n so that the result gets promoted to an array if n is an array, # which hopefully means this function will have a fixed return type given the @@ -98,8 +97,8 @@ function krook_collisions!(pdf_out, fvec_in, moments, composition, collisions, v # through by vth, remembering pdf is already multiplied by vth @loop_s_r_z is ir iz begin n = fvec_in.density[iz,ir,is] - vth = moments.charged.vth[iz,ir,is] - nu_ii = get_collision_frequency(collisions, n, vth) + vth = moments.ion.vth[iz,ir,is] + nu_ii = get_collision_frequency_ii(collisions, n, vth) @loop_vperp_vpa ivperp ivpa begin pdf_out[ivpa,ivperp,iz,ir,is] -= dt * nu_ii * (fvec_in.pdf[ivpa,ivperp,iz,ir,is] @@ -111,8 +110,8 @@ function krook_collisions!(pdf_out, fvec_in, moments, composition, collisions, v # is already multiplied by vth, and grid is already normalized by vth @loop_s_r_z is ir iz begin n = fvec_in.density[iz,ir,is] - vth = moments.charged.vth[iz,ir,is] - nu_ii = get_collision_frequency(collisions, n, vth) + vth = moments.ion.vth[iz,ir,is] + nu_ii = get_collision_frequency_ii(collisions, n, vth) @loop_vperp_vpa ivperp ivpa begin pdf_out[ivpa,ivperp,iz,ir,is] -= dt * nu_ii * (fvec_in.pdf[ivpa,ivperp,iz,ir,is] @@ -124,8 +123,8 @@ function krook_collisions!(pdf_out, fvec_in, moments, composition, collisions, v # Compared to evolve_density version, grid is already shifted by upar @loop_s_r_z is ir iz begin n = fvec_in.density[iz,ir,is] - vth = moments.charged.vth[iz,ir,is] - nu_ii = get_collision_frequency(collisions, n, vth) + vth = moments.ion.vth[iz,ir,is] + nu_ii = get_collision_frequency_ii(collisions, n, vth) @loop_vperp_vpa ivperp ivpa begin pdf_out[ivpa,ivperp,iz,ir,is] -= dt * nu_ii * (fvec_in.pdf[ivpa,ivperp,iz,ir,is] @@ -138,8 +137,8 @@ function krook_collisions!(pdf_out, fvec_in, moments, composition, collisions, v # that pdf is already normalized by density @loop_s_r_z is ir iz begin n = fvec_in.density[iz,ir,is] - vth = moments.charged.vth[iz,ir,is] - nu_ii = get_collision_frequency(collisions, n, vth) + vth = moments.ion.vth[iz,ir,is] + nu_ii = get_collision_frequency_ii(collisions, n, vth) @loop_vperp_vpa ivperp ivpa begin pdf_out[ivpa,ivperp,iz,ir,is] -= dt * nu_ii * (fvec_in.pdf[ivpa,ivperp,iz,ir,is] @@ -151,13 +150,13 @@ function krook_collisions!(pdf_out, fvec_in, moments, composition, collisions, v else @loop_s_r_z is ir iz begin n = fvec_in.density[iz,ir,is] - vth = moments.charged.vth[iz,ir,is] + vth = moments.ion.vth[iz,ir,is] if vperp.n == 1 vth_prefactor = 1.0 / vth else vth_prefactor = 1.0 / vth^3 end - nu_ii = get_collision_frequency(collisions, n, vth) + nu_ii = get_collision_frequency_ii(collisions, n, vth) @loop_vperp_vpa ivperp ivpa begin pdf_out[ivpa,ivperp,iz,ir,is] -= dt * nu_ii * (fvec_in.pdf[ivpa,ivperp,iz,ir,is] diff --git a/moment_kinetics/src/load_data.jl b/moment_kinetics/src/load_data.jl index 2ce2a5546..91992e9c6 100644 --- a/moment_kinetics/src/load_data.jl +++ b/moment_kinetics/src/load_data.jl @@ -4,7 +4,7 @@ module load_data export open_readonly_output_file export load_fields_data -export load_charged_particle_moments_data +export load_ion_particle_moments_data export load_neutral_particle_moments_data export load_pdf_data export load_neutral_pdf_data @@ -17,12 +17,12 @@ export read_distributed_zr_data! using ..array_allocation: allocate_float, allocate_int using ..calculus: derivative! -using ..communication: setup_distributed_memory_MPI +using ..communication using ..coordinates: coordinate, define_coordinate using ..file_io: check_io_implementation, get_group, get_subgroup_keys, get_variable_keys -using ..krook_collisions: get_collision_frequency -using ..input_structs: advection_input, grid_input, hdf5, netcdf +using ..input_structs using ..interpolation: interpolate_to_grid_1d! +using ..krook_collisions using ..looping using ..moment_kinetics_input: mk_input using ..neutral_vz_advection: update_speed_neutral_vz! @@ -45,7 +45,7 @@ const timestep_diagnostic_variables = ("time_for_run", "step_counter", "dt", const em_variables = ("phi", "Er", "Ez") const ion_moment_variables = ("density", "parallel_flow", "parallel_pressure", "thermal_speed", "temperature", "parallel_heat_flux", - "collision_frequency", "sound_speed", "mach_number") + "collision_frequency_ii", "sound_speed", "mach_number") const neutral_moment_variables = ("density_neutral", "uz_neutral", "pz_neutral", "thermal_speed_neutral", "temperature_neutral", "qz_neutral") @@ -186,7 +186,8 @@ function load_input(fid) end """ - load_coordinate_data(fid, name; printout=false, irank=nothing, nrank=nothing) + load_coordinate_data(fid, name; printout=false, irank=nothing, nrank=nothing, + run_directory=nothing, ignore_MPI=true) Load data for the coordinate `name` from a file-handle `fid`. @@ -199,6 +200,10 @@ If `printout` is set to `true` a message will be printed when this function is c If `irank` and `nrank` are passed, then the `coord` and `spectral` objects returned will be set up for the parallelisation specified by `irank` and `nrank`, rather than the one implied by the output file. + +Unless `ignore_MPI=false` is passed, the returned coordinates will be created without +shared memory scratch arrays (`ignore_MPI=true` will be passed through to +[`define_coordinate`](@ref)). """ function load_coordinate_data(fid, name; printout=false, irank=nothing, nrank=nothing, run_directory=nothing, ignore_MPI=true) @@ -209,7 +214,12 @@ function load_coordinate_data(fid, name; printout=false, irank=nothing, nrank=no overview = get_group(fid, "overview") parallel_io = load_variable(overview, "parallel_io") - coord_group = get_group(get_group(fid, "coords"), name) + coords_group = get_group(fid, "coords") + if name ∈ get_subgroup_keys(coords_group) + coord_group = get_group(coords_group, name) + else + return nothing, nothing, nothing + end ngrid = load_variable(coord_group, "ngrid") n_local = load_variable(coord_group, "n_local") @@ -290,7 +300,8 @@ function load_coordinate_data(fid, name; printout=false, irank=nothing, nrank=no advection_input("default", 0.0, 0.0, 0.0), MPI.COMM_NULL, element_spacing_option) - coord, spectral = define_coordinate(input, parallel_io; ignore_MPI=ignore_MPI) + coord, spectral = define_coordinate(input, parallel_io; run_directory=run_directory, + ignore_MPI=ignore_MPI) return coord, spectral, chunk_size end @@ -441,33 +452,33 @@ end """ """ -function load_charged_particle_moments_data(fid; printout=false, extended_moments = false) +function load_ion_moments_data(fid; printout=false, extended_moments = false) if printout - print("Loading charged particle velocity moments data...") + print("Loading ion velocity moments data...") end group = get_group(fid, "dynamic_data") - # Read charged species density + # Read ion species density density = load_variable(group, "density") - # Read charged species parallel flow + # Read ion species parallel flow parallel_flow = load_variable(group, "parallel_flow") - # Read charged species parallel pressure + # Read ion species parallel pressure parallel_pressure = load_variable(group, "parallel_pressure") - # Read charged_species parallel heat flux + # Read ion_species parallel heat flux parallel_heat_flux = load_variable(group, "parallel_heat_flux") - # Read charged species thermal speed + # Read ion species thermal speed thermal_speed = load_variable(group, "thermal_speed") if extended_moments - # Read charged species perpendicular pressure + # Read ion species perpendicular pressure perpendicular_pressure = load_variable(group, "perpendicular_pressure") - # Read charged species entropy_production + # Read ion species entropy_production entropy_production = load_variable(group, "entropy_production") end @@ -475,7 +486,7 @@ function load_charged_particle_moments_data(fid; printout=false, extended_moment println("done.") end if extended_moments - density, parallel_flow, parallel_pressure, perpendicular_pressure, parallel_heat_flux, thermal_speed, entropy_production + return density, parallel_flow, parallel_pressure, perpendicular_pressure, parallel_heat_flux, thermal_speed, entropy_production else return density, parallel_flow, parallel_pressure, parallel_heat_flux, thermal_speed end @@ -514,12 +525,12 @@ end """ function load_pdf_data(fid; printout=false) if printout - print("Loading charged particle distribution function data...") + print("Loading ion particle distribution function data...") end group = get_group(fid, "dynamic_data") - # Read charged distribution function + # Read ion distribution function pdf = load_variable(group, "f") if printout @@ -574,86 +585,26 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, previous_runs_info = load_run_info_history(fid) restart_n_ion_species, restart_n_neutral_species = load_species_data(fid) - if parallel_io - restart_z, restart_z_spectral, _ = - load_coordinate_data(fid, "z"; irank=z.irank, nrank=z.nrank) - restart_r, restart_r_spectral, _ = - load_coordinate_data(fid, "r"; irank=r.irank, nrank=r.nrank) - restart_vperp, restart_vperp_spectral, _ = - load_coordinate_data(fid, "vperp"; irank=vperp.irank, - nrank=vperp.nrank) - restart_vpa, restart_vpa_spectral, _ = - load_coordinate_data(fid, "vpa"; irank=vpa.irank, nrank=vpa.nrank) - restart_vzeta, restart_vzeta_spectral, _ = - load_coordinate_data(fid, "vzeta"; irank=vzeta.irank, - nrank=vzeta.nrank) - restart_vr, restart_vr_spectral, _ = - load_coordinate_data(fid, "vr"; irank=vr.irank, nrank=vr.nrank) - restart_vz, restart_vz_spectral, _ = - load_coordinate_data(fid, "vz"; irank=vz.irank, nrank=vz.nrank) - else - restart_z, restart_z_spectral, _ = - load_coordinate_data(fid, "z") - restart_r, restart_r_spectral, _ = - load_coordinate_data(fid, "r") - restart_vperp, restart_vperp_spectral, _ = - load_coordinate_data(fid, "vperp") - restart_vpa, restart_vpa_spectral, _ = - load_coordinate_data(fid, "vpa") - restart_vzeta, restart_vzeta_spectral, _ = - load_coordinate_data(fid, "vzeta") - restart_vr, restart_vr_spectral, _ = - load_coordinate_data(fid, "vr") - restart_vz, restart_vz_spectral, _ = - load_coordinate_data(fid, "vz") - - if restart_r.nrank != r.nrank - error("Not using parallel I/O, and distributed MPI layout has " - * "changed: now r.nrank=$(r.nrank), but we are trying to " - * "restart from files ith restart_r.nrank=$(restart_r.nrank).") - end - if restart_z.nrank != z.nrank - error("Not using parallel I/O, and distributed MPI layout has " - * "changed: now z.nrank=$(z.nrank), but we are trying to " - * "restart from files ith restart_z.nrank=$(restart_z.nrank).") - end - if restart_vperp.nrank != vperp.nrank - error("Not using parallel I/O, and distributed MPI layout has " - * "changed: now vperp.nrank=$(vperp.nrank), but we are trying to " - * "restart from files ith restart_vperp.nrank=$(restart_vperp.nrank).") - end - if restart_vpa.nrank != vpa.nrank - error("Not using parallel I/O, and distributed MPI layout has " - * "changed: now vpa.nrank=$(vpa.nrank), but we are trying to " - * "restart from files ith restart_vpa.nrank=$(restart_vpa.nrank).") - end - if restart_vzeta.nrank != vzeta.nrank - error("Not using parallel I/O, and distributed MPI layout has " - * "changed: now vzeta.nrank=$(vzeta.nrank), but we are trying to " - * "restart from files ith restart_vzeta.nrank=$(restart_vzeta.nrank).") - end - if restart_vr.nrank != vr.nrank - error("Not using parallel I/O, and distributed MPI layout has " - * "changed: now vr.nrank=$(vr.nrank), but we are trying to " - * "restart from files ith restart_vr.nrank=$(restart_vr.nrank).") - end - if restart_vz.nrank != vz.nrank - error("Not using parallel I/O, and distributed MPI layout has " - * "changed: now vz.nrank=$(vz.nrank), but we are trying to " - * "restart from files ith restart_vz.nrank=$(restart_vz.nrank).") - end - end + restart_r, restart_r_spectral, restart_z, restart_z_spectral, restart_vperp, + restart_vperp_spectral, restart_vpa, restart_vpa_spectral, restart_vzeta, + restart_vzeta_spectral, restart_vr,restart_vr_spectral, restart_vz, + restart_vz_spectral = load_restart_coordinates(fid, r, z, vperp, vpa, + vzeta, vr, vz, parallel_io) # Test whether any interpolation is needed interpolation_needed = Dict( - x.name => x.n != restart_x.n || !all(isapprox.(x.grid, restart_x.grid)) + x.name => (restart_x !== nothing + && (x.n != restart_x.n + || !all(isapprox.(x.grid, restart_x.grid)))) for (x, restart_x) ∈ ((z, restart_z), (r, restart_r), (vperp, restart_vperp), (vpa, restart_vpa), (vzeta, restart_vzeta), (vr, restart_vr), (vz, restart_vz))) neutral_1V = (vzeta.n_global == 1 && vr.n_global == 1) - restart_neutral_1V = (restart_vzeta.n_global == 1 && restart_vr.n_global == 1) + restart_neutral_1V = ((restart_vzeta === nothing + || restart_vzeta.n_global == 1) + && (restart_vr === nothing || restart_vr.n_global == 1)) if any(geometry.bzeta .!= 0.0) && ((neutral_1V && !restart_neutral_1V) || (!neutral_1V && restart_neutral_1V)) # One but not the other of the run being restarted from and this run are @@ -666,614 +617,184 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, code_time = load_slice(dynamic, "time", time_index) - if parallel_io - function get_range(coord) - if coord.irank == coord.nrank - 1 - return coord.global_io_range - else - # Need to modify the range to load the end-point that is duplicated on - # the next process - this_range = coord.global_io_range - return this_range.start:(this_range.stop+1) - end + r_range, z_range, vperp_range, vpa_range, vzeta_range, vr_range, vz_range = + get_reload_ranges(parallel_io, restart_r, restart_z, restart_vperp, + restart_vpa, restart_vzeta, restart_vr, restart_vz) + + moments.ion.dens .= reload_moment("density", dynamic, time_index, r, z, + r_range, z_range, restart_r, + restart_r_spectral, restart_z, + restart_z_spectral, interpolation_needed) + moments.ion.dens_updated .= true + moments.ion.upar .= reload_moment("parallel_flow", dynamic, time_index, r, z, + r_range, z_range, restart_r, + restart_r_spectral, restart_z, + restart_z_spectral, interpolation_needed) + moments.ion.upar_updated .= true + moments.ion.ppar .= reload_moment("parallel_pressure", dynamic, time_index, r, + z, r_range, z_range, restart_r, + restart_r_spectral, restart_z, + restart_z_spectral, interpolation_needed) + moments.ion.ppar_updated .= true + moments.ion.qpar .= reload_moment("parallel_heat_flux", dynamic, time_index, + r, z, r_range, z_range, restart_r, + restart_r_spectral, restart_z, + restart_z_spectral, interpolation_needed) + moments.ion.qpar_updated .= true + moments.ion.vth .= reload_moment("thermal_speed", dynamic, time_index, r, z, + r_range, z_range, restart_r, + restart_r_spectral, restart_z, + restart_z_spectral, interpolation_needed) + moments.ion.dSdt .= reload_moment("entropy_production", dynamic, time_index, + r, z, r_range, z_range, restart_r, + restart_r_spectral, restart_z, + restart_z_spectral, interpolation_needed) + if moments.evolve_density || moments.evolve_upar || moments.evolve_ppar + if "ion_constraints_A_coefficient" ∈ keys(dynamic) + moments.ion.constraints_A_coefficient .= + reload_moment("ion_constraints_A_coefficient", dynamic, + time_index, r, z, r_range, z_range, restart_r, + restart_r_spectral, restart_z, restart_z_spectral, + interpolation_needed) + elseif moments.ion.constraints_A_coefficient !== nothing + moments.ion.constraints_A_coefficient .= 0.0 end - r_range = get_range(restart_r) - z_range = get_range(restart_z) - vperp_range = get_range(restart_vperp) - vpa_range = get_range(restart_vpa) - vzeta_range = get_range(restart_vzeta) - vr_range = get_range(restart_vr) - vz_range = get_range(restart_vz) - else - r_range = (:) - z_range = (:) - vperp_range = (:) - vpa_range = (:) - vzeta_range = (:) - vr_range = (:) - vz_range = (:) - end - - function load_moment(var_name) - moment = load_slice(dynamic, var_name, z_range, r_range, :, time_index) - orig_nz, orig_nr, nspecies = size(moment) - if interpolation_needed["r"] - new_moment = allocate_float(orig_nz, r.n, nspecies) - for is ∈ 1:nspecies, iz ∈ 1:orig_nz - @views interpolate_to_grid_1d!(new_moment[iz,:,is], r.grid, - moment[iz,:,is], restart_r, - restart_r_spectral) - end - moment = new_moment + if "ion_constraints_B_coefficient" ∈ keys(dynamic) + moments.ion.constraints_B_coefficient .= + reload_moment("ion_constraints_B_coefficient", dynamic, + time_index, r, z, r_range, z_range, restart_r, + restart_r_spectral, restart_z, restart_z_spectral, + interpolation_needed) + elseif moments.ion.constraints_B_coefficient !== nothing + moments.ion.constraints_B_coefficient .= 0.0 end - if interpolation_needed["z"] - new_moment = allocate_float(z.n, r.n, nspecies) - for is ∈ 1:nspecies, ir ∈ 1:r.n - @views interpolate_to_grid_1d!(new_moment[:,ir,is], z.grid, - moment[:,ir,is], restart_z, - restart_z_spectral) - end - moment = new_moment + if "ion_constraints_C_coefficient" ∈ keys(dynamic) + moments.ion.constraints_C_coefficient .= + reload_moment("ion_constraints_C_coefficient", dynamic, + time_index, r, z, r_range, z_range, restart_r, + restart_r_spectral, restart_z, restart_z_spectral, + interpolation_needed) + elseif moments.ion.constraints_C_coefficient !== nothing + moments.ion.constraints_C_coefficient .= 0.0 end - return moment end - - moments.charged.dens .= load_moment("density") - moments.charged.dens_updated .= true - moments.charged.upar .= load_moment("parallel_flow") - moments.charged.upar_updated .= true - moments.charged.ppar .= load_moment("parallel_pressure") - moments.charged.ppar_updated .= true - moments.charged.qpar .= load_moment("parallel_heat_flux") - moments.charged.qpar_updated .= true - moments.charged.vth .= load_moment("thermal_speed") if z.irank == 0 if "chodura_integral_lower" ∈ keys(dynamic) - moments.charged.chodura_integral_lower .= load_slice(dynamic, "chodura_integral_lower", - r_range, :, time_index) + moments.ion.chodura_integral_lower .= load_slice(dynamic, "chodura_integral_lower", + r_range, :, time_index) else - moments.charged.chodura_integral_lower .= 0.0 + moments.ion.chodura_integral_lower .= 0.0 end end if z.irank == z.nrank - 1 if "chodura_integral_upper" ∈ keys(dynamic) - moments.charged.chodura_integral_upper .= load_slice(dynamic, "chodura_integral_upper", - r_range, :, time_index) + moments.ion.chodura_integral_upper .= load_slice(dynamic, "chodura_integral_upper", + r_range, :, time_index) else - moments.charged.chodura_integral_upper .= 0.0 + moments.ion.chodura_integral_upper .= 0.0 end end if "external_source_controller_integral" ∈ get_variable_keys(dynamic) && - length(moments.charged.external_source_controller_integral) == 1 - moments.charged.external_source_controller_integral .= + length(moments.ion.external_source_controller_integral) == 1 + moments.ion.external_source_controller_integral .= load_slice(dynamic, "external_source_controller_integral", time_index) - elseif length(moments.charged.external_source_controller_integral) > 1 - moments.charged.external_source_controller_integral .= - load_moment("external_source_controller_integral") + elseif length(moments.ion.external_source_controller_integral) > 1 + moments.ion.external_source_controller_integral .= + reload_moment("external_source_controller_integral", dynamic, + time_index, r, z, r_range, z_range, restart_r, + restart_r_spectral, restart_z, restart_z_spectral, + interpolation_needed) end - function load_charged_pdf() - this_pdf = load_slice(dynamic, "f", vpa_range, vperp_range, z_range, - r_range, :, time_index) - orig_nvpa, orig_nvperp, orig_nz, orig_nr, nspecies = size(this_pdf) - if interpolation_needed["r"] - new_pdf = allocate_float(orig_nvpa, orig_nvperp, orig_nz, r.n, nspecies) - for is ∈ 1:nspecies, iz ∈ 1:orig_nz, ivperp ∈ 1:orig_nvperp, - ivpa ∈ 1:orig_nvpa - @views interpolate_to_grid_1d!( - new_pdf[ivpa,ivperp,iz,:,is], r.grid, - this_pdf[ivpa,ivperp,iz,:,is], restart_r, - restart_r_spectral) - end - this_pdf = new_pdf - end - if interpolation_needed["z"] - new_pdf = allocate_float(orig_nvpa, orig_nvperp, z.n, r.n, nspecies) - for is ∈ 1:nspecies, ir ∈ 1:r.n, ivperp ∈ 1:orig_nvperp, - ivpa ∈ 1:orig_nvpa - @views interpolate_to_grid_1d!( - new_pdf[ivpa,ivperp,:,ir,is], z.grid, - this_pdf[ivpa,ivperp,:,ir,is], restart_z, - restart_z_spectral) - end - this_pdf = new_pdf - end - - # Current moment-kinetic implementation is only 1V, so no need to handle a - # normalised vperp coordinate. This will need to change when 2V - # moment-kinetics is implemented. - if interpolation_needed["vperp"] - new_pdf = allocate_float(orig_nvpa, vperp.n, z.n, r.n, nspecies) - for is ∈ 1:nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivpa ∈ 1:orig_nvpa - @views interpolate_to_grid_1d!( - new_pdf[ivpa,:,iz,ir,is], vperp.grid, - this_pdf[ivpa,:,iz,ir,is], restart_vperp, - restart_vperp_spectral) - end - this_pdf = new_pdf - end - - if ( - (moments.evolve_density == restart_evolve_density && - moments.evolve_upar == restart_evolve_upar && moments.evolve_ppar == - restart_evolve_ppar) - || (!moments.evolve_upar && !restart_evolve_upar && - !moments.evolve_ppar && !restart_evolve_ppar) - ) - # No chages to velocity-space coordinates, so just interpolate from - # one grid to the other - if interpolation_needed["vpa"] - new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) - for is ∈ 1:nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,ir,is], vpa.grid, - this_pdf[:,ivperp,iz,ir,is], restart_vpa, - restart_vpa_spectral) - end - this_pdf = new_pdf - end - elseif (!moments.evolve_upar && !moments.evolve_ppar && - restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa = old_wpa + upar - # => old_wpa = new_wpa - upar - new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = vpa.grid .- moments.charged.upar[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,ir,is], restart_vpa, - restart_vpa_spectral) - end - this_pdf = new_pdf - elseif (!moments.evolve_upar && !moments.evolve_ppar && - !restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa = old_wpa*vth - # => old_wpa = new_wpa/vth - new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = vpa.grid ./ moments.charged.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,ir,is], restart_vpa, - restart_vpa_spectral) - end - this_pdf = new_pdf - elseif (!moments.evolve_upar && !moments.evolve_ppar && - restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa = old_wpa*vth + upar - # => old_wpa = (new_wpa - upar)/vth - new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = - @. (vpa.grid - moments.charged.upar[iz,ir,is]) / - moments.charged.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,ir,is], restart_vpa, - restart_vpa_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && !moments.evolve_ppar && - !restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa + upar = old_wpa - # => old_wpa = new_wpa + upar - new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = vpa.grid .+ moments.charged.upar[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,ir,is], restart_vpa, - restart_vpa_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && !moments.evolve_ppar && - !restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa + upar = old_wpa*vth - # => old_wpa = (new_wpa + upar)/vth - new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = - @. (vpa.grid + moments.charged.upar[iz,ir,is]) / - moments.charged.vth - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,ir,is], restart_vpa, - restart_vpa_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && !moments.evolve_ppar && - restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa + upar = old_wpa*vth + upar - # => old_wpa = new_wpa/vth - new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = vpa.grid ./ moments.charged.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,ir,is], restart_vpa, - restart_vpa_spectral) - end - this_pdf = new_pdf - elseif (!moments.evolve_upar && moments.evolve_ppar && - !restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa*vth = old_wpa - # => old_wpa = new_wpa*vth - new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = vpa.grid .* moments.charged.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,ir,is], restart_vpa, - restart_vpa_spectral) - end - this_pdf = new_pdf - elseif (!moments.evolve_upar && moments.evolve_ppar && - restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa*vth = old_wpa + upar - # => old_wpa = new_wpa*vth - upar/vth - new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = @. vpa.grid * moments.charged.vth[iz,ir,is] - - moments.charged.upar[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,ir,is], restart_vpa, - restart_vpa_spectral) - end - this_pdf = new_pdf - elseif (!moments.evolve_upar && moments.evolve_ppar && - restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa*vth = old_wpa*vth + upar - # => old_wpa = new_wpa - upar/vth - new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = - @. vpa.grid - - moments.charged.upar[iz,ir,is]/moments.charged.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,ir,is], restart_vpa, - restart_vpa_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && moments.evolve_ppar && - !restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa*vth + upar = old_wpa - # => old_wpa = new_wpa*vth + upar - new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = @. vpa.grid * moments.charged.vth[iz,ir,is] + - moments.charged.upar[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,ir,is], restart_vpa, - restart_vpa_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && moments.evolve_ppar && - restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa*vth + upar = old_wpa + upar - # => old_wpa = new_wpa*vth - new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = vpa.grid .* moments.charged.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,ir,is], restart_vpa, - restart_vpa_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && moments.evolve_ppar && - !restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa*vth + upar = old_wpa*vth - # => old_wpa = new_wpa + upar/vth - new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = - @. vpa.grid + - moments.charged.upar[iz,ir,is] / moments.charged.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,ir,is], restart_vpa, - restart_vpa_spectral) - end - this_pdf = new_pdf - else - # This should never happen, as all combinations of evolve_* options - # should be handled above. - error("Unsupported combination of moment-kinetic options:" - * " evolve_density=", moments.evolve_density - * " evolve_upar=", moments.evolve_upar - * " evolve_ppar=", moments.evolve_ppar - * " restart_evolve_density=", restart_evolve_density - * " restart_evolve_upar=", restart_evolve_upar - * " restart_evolve_ppar=", restart_evolve_ppar) - end - if moments.evolve_density && !restart_evolve_density - # Need to normalise by density - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n - this_pdf[:,:,iz,ir,is] ./= moments.charged.dens[iz,ir,is] - end - elseif !moments.evolve_density && restart_evolve_density - # Need to unnormalise by density - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n - this_pdf[:,:,iz,ir,is] .*= moments.charged.dens[iz,ir,is] - end - end - if moments.evolve_ppar && !restart_evolve_ppar - # Need to normalise by vth - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n - this_pdf[:,:,iz,ir,is] .*= moments.charged.vth[iz,ir,is] - end - elseif !moments.evolve_ppar && restart_evolve_ppar - # Need to unnormalise by vth - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n - this_pdf[:,:,iz,ir,is] ./= moments.charged.vth[iz,ir,is] - end - end - - return this_pdf - end - - pdf.charged.norm .= load_charged_pdf() + pdf.ion.norm .= reload_ion_pdf(dynamic, time_index, moments, r, z, vperp, vpa, r_range, + z_range, vperp_range, vpa_range, restart_r, + restart_r_spectral, restart_z, + restart_z_spectral, restart_vperp, + restart_vperp_spectral, restart_vpa, + restart_vpa_spectral, interpolation_needed, + restart_evolve_density, restart_evolve_upar, + restart_evolve_ppar) boundary_distributions_io = get_group(fid, "boundary_distributions") - function load_charged_boundary_pdf(var_name, ir) - this_pdf = load_slice(boundary_distributions_io, var_name, vpa_range, - vperp_range, z_range, :) - orig_nvpa, orig_nvperp, orig_nz, nspecies = size(this_pdf) - if interpolation_needed["z"] - new_pdf = allocate_float(orig_nvpa, orig_nvperp, z.n, nspecies) - for is ∈ 1:nspecies, ivperp ∈ 1:orig_nvperp, - ivpa ∈ 1:orig_nvpa - @views interpolate_to_grid_1d!( - new_pdf[ivpa,ivperp,:,is], z.grid, - this_pdf[ivpa,ivperp,:,is], restart_z, - restart_z_spectral) - end - this_pdf = new_pdf - end - - # Current moment-kinetic implementation is only 1V, so no need to handle a - # normalised vperp coordinate. This will need to change when 2V - # moment-kinetics is implemented. - if interpolation_needed["vperp"] - new_pdf = allocate_float(orig_nvpa, vperp.n, z.n, nspecies) - for is ∈ 1:nspecies, iz ∈ 1:z.n, ivpa ∈ 1:orig_nvpa - @views interpolate_to_grid_1d!( - new_pdf[ivpa,:,iz,is], vperp.grid, - this_pdf[ivpa,:,iz,is], restart_vperp, - restart_vperp_spectral) - end - this_pdf = new_pdf - end - - if ( - (moments.evolve_density == restart_evolve_density && - moments.evolve_upar == restart_evolve_upar && moments.evolve_ppar == - restart_evolve_ppar) - || (!moments.evolve_upar && !restart_evolve_upar && - !moments.evolve_ppar && !restart_evolve_ppar) - ) - # No chages to velocity-space coordinates, so just interpolate from - # one grid to the other - if interpolation_needed["vpa"] - new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) - for is ∈ 1:nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,is], vpa.grid, - this_pdf[:,ivperp,iz,is], restart_vpa, - restart_vpa_spectral) - end - this_pdf = new_pdf - end - elseif (!moments.evolve_upar && !moments.evolve_ppar && - restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa = old_wpa + upar - # => old_wpa = new_wpa - upar - new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = vpa.grid .- moments.charged.upar[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) - end - this_pdf = new_pdf - elseif (!moments.evolve_upar && !moments.evolve_ppar && - !restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa = old_wpa*vth - # => old_wpa = new_wpa/vth - new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = vpa.grid ./ moments.charged.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) - end - this_pdf = new_pdf - elseif (!moments.evolve_upar && !moments.evolve_ppar && - restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa = old_wpa*vth + upar - # => old_wpa = (new_wpa - upar)/vth - new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = - @. (vpa.grid - moments.charged.upar[iz,ir,is]) / - moments.charged.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && !moments.evolve_ppar && - !restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa + upar = old_wpa - # => old_wpa = new_wpa + upar - new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = vpa.grid .+ moments.charged.upar[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && !moments.evolve_ppar && - !restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa + upar = old_wpa*vth - # => old_wpa = (new_wpa + upar)/vth - new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = - @. (vpa.grid + moments.charged.upar[iz,ir,is]) / - moments.charged.vth - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && !moments.evolve_ppar && - restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa + upar = old_wpa*vth + upar - # => old_wpa = new_wpa/vth - new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = vpa.grid ./ moments.charged.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) - end - this_pdf = new_pdf - elseif (!moments.evolve_upar && moments.evolve_ppar && - !restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa*vth = old_wpa - # => old_wpa = new_wpa*vth - new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = vpa.grid .* moments.charged.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) - end - this_pdf = new_pdf - elseif (!moments.evolve_upar && moments.evolve_ppar && - restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa*vth = old_wpa + upar - # => old_wpa = new_wpa*vth - upar/vth - new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = @. vpa.grid * moments.charged.vth[iz,ir,is] - - moments.charged.upar[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) - end - this_pdf = new_pdf - elseif (!moments.evolve_upar && moments.evolve_ppar && - restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa*vth = old_wpa*vth + upar - # => old_wpa = new_wpa - upar/vth - new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = - @. vpa.grid - - moments.charged.upar[iz,ir,is]/moments.charged.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && moments.evolve_ppar && - !restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa*vth + upar = old_wpa - # => old_wpa = new_wpa*vth + upar - new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = @. vpa.grid * moments.charged.vth[iz,ir,is] + - moments.charged.upar[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && moments.evolve_ppar && - restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa*vth + upar = old_wpa + upar - # => old_wpa = new_wpa*vth - new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = vpa.grid .* moments.charged.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && moments.evolve_ppar && - !restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa*vth + upar = old_wpa*vth - # => old_wpa = new_wpa + upar/vth - new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n - restart_vpa_vals = - @. vpa.grid + - moments.charged.upar[iz,ir,is] / moments.charged.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivperp,iz,is], restart_vpa_vals, - this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) - end - this_pdf = new_pdf - else - # This should never happen, as all combinations of evolve_* options - # should be handled above. - error("Unsupported combination of moment-kinetic options:" - * " evolve_density=", moments.evolve_density - * " evolve_upar=", moments.evolve_upar - * " evolve_ppar=", moments.evolve_ppar - * " restart_evolve_density=", restart_evolve_density - * " restart_evolve_upar=", restart_evolve_upar - * " restart_evolve_ppar=", restart_evolve_ppar) - end - if moments.evolve_density && !restart_evolve_density - # Need to normalise by density - for is ∈ nspecies, iz ∈ 1:z.n - this_pdf[:,:,iz,is] ./= moments.charged.dens[iz,ir,is] - end - elseif !moments.evolve_density && restart_evolve_density - # Need to unnormalise by density - for is ∈ nspecies, iz ∈ 1:z.n - this_pdf[:,:,iz,is] .*= moments.charged.dens[iz,ir,is] - end - end - if moments.evolve_ppar && !restart_evolve_ppar - # Need to normalise by vth - for is ∈ nspecies, iz ∈ 1:z.n - this_pdf[:,:,iz,is] .*= moments.charged.vth[iz,ir,is] - end - elseif !moments.evolve_ppar && restart_evolve_ppar - # Need to unnormalise by vth - for is ∈ nspecies, iz ∈ 1:z.n - this_pdf[:,:,iz,is] ./= moments.charged.vth[iz,ir,is] - end - end - - return this_pdf - end - - boundary_distributions.pdf_rboundary_charged[:,:,:,1,:] .= - load_charged_boundary_pdf("pdf_rboundary_charged_left", 1) - boundary_distributions.pdf_rboundary_charged[:,:,:,2,:] .= - load_charged_boundary_pdf("pdf_rboundary_charged_right", r.n) + boundary_distributions.pdf_rboundary_ion[:,:,:,1,:] .= + reload_ion_boundary_pdf(boundary_distributions_io, + "pdf_rboundary_ion_left", 1, moments, z, vperp, + vpa, z_range, vperp_range, vpa_range, restart_z, + restart_z_spectral, restart_vperp, + restart_vperp_spectral, restart_vpa, + restart_vpa_spectral, interpolation_needed, + restart_evolve_density, restart_evolve_upar, + restart_evolve_ppar) + boundary_distributions.pdf_rboundary_ion[:,:,:,2,:] .= + reload_ion_boundary_pdf(boundary_distributions_io, + "pdf_rboundary_ion_right", r.n, moments, z, vperp, + vpa, z_range, vperp_range, vpa_range, restart_z, + restart_z_spectral, restart_vperp, + restart_vperp_spectral, restart_vpa, + restart_vpa_spectral, interpolation_needed, + restart_evolve_density, restart_evolve_upar, + restart_evolve_ppar) if composition.n_neutral_species > 0 - moments.neutral.dens .= load_moment("density_neutral") + moments.neutral.dens .= reload_moment("density_neutral", dynamic, + time_index, r, z, r_range, z_range, + restart_r, restart_r_spectral, + restart_z, restart_z_spectral, + interpolation_needed) moments.neutral.dens_updated .= true - moments.neutral.uz .= load_moment("uz_neutral") + moments.neutral.uz .= reload_moment("uz_neutral", dynamic, time_index, r, + z, r_range, z_range, restart_r, + restart_r_spectral, restart_z, + restart_z_spectral, + interpolation_needed) moments.neutral.uz_updated .= true - moments.neutral.pz .= load_moment("pz_neutral") + moments.neutral.pz .= reload_moment("pz_neutral", dynamic, time_index, r, + z, r_range, z_range, restart_r, + restart_r_spectral, restart_z, + restart_z_spectral, + interpolation_needed) moments.neutral.pz_updated .= true - moments.neutral.qz .= load_moment("qz_neutral") + moments.neutral.qz .= reload_moment("qz_neutral", dynamic, time_index, r, + z, r_range, z_range, restart_r, + restart_r_spectral, restart_z, + restart_z_spectral, + interpolation_needed) moments.neutral.qz_updated .= true - moments.neutral.vth .= load_moment("thermal_speed_neutral") + moments.neutral.vth .= reload_moment("thermal_speed_neutral", dynamic, + time_index, r, z, r_range, z_range, + restart_r, restart_r_spectral, + restart_z, restart_z_spectral, + interpolation_needed) + if moments.evolve_density || moments.evolve_upar || moments.evolve_ppar + if "neutral_constraints_A_coefficient" ∈ keys(dynamic) + moments.neutral.constraints_A_coefficient .= + reload_moment("neutral_constraints_A_coefficient", dynamic, + time_index, r, z, r_range, z_range, restart_r, + restart_r_spectral, restart_z, restart_z_spectral, + interpolation_needed) + elseif moments.neutral.constraints_A_coefficient !== nothing + moments.neutral.constraints_A_coefficient .= 0.0 + end + if "neutral_constraints_B_coefficient" ∈ keys(dynamic) + moments.neutral.constraints_B_coefficient .= + reload_moment("neutral_constraints_B_coefficient", dynamic, + time_index, r, z, r_range, z_range, restart_r, + restart_r_spectral, restart_z, restart_z_spectral, + interpolation_needed) + elseif moments.neutral.constraints_B_coefficient !== nothing + moments.neutral.constraints_B_coefficient .= 0.0 + end + if "neutral_constraints_C_coefficient" ∈ keys(dynamic) + moments.neutral.constraints_C_coefficient .= + reload_moment("neutral_constraints_C_coefficient", dynamic, + time_index, r, z, r_range, z_range, restart_r, + restart_r_spectral, restart_z, restart_z_spectral, + interpolation_needed) + elseif moments.neutral.constraints_C_coefficient !== nothing + moments.neutral.constraints_C_coefficient .= 0.0 + end + end if "external_source_neutral_controller_integral" ∈ get_variable_keys(dynamic) && length(moments.neutral.external_source_controller_integral) == 1 @@ -1283,579 +804,46 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, time_index) elseif length(moments.neutral.external_source_controller_integral) > 1 moments.neutral.external_source_controller_integral .= - load_moment("external_source_neutral_controller_integral") - end - - function load_neutral_pdf() - this_pdf = load_slice(dynamic, "f_neutral", vz_range, vr_range, - vzeta_range, z_range, r_range, :, time_index) - orig_nvz, orig_nvr, orig_nvzeta, orig_nz, orig_nr, nspecies = - size(this_pdf) - if interpolation_needed["r"] - new_pdf = allocate_float(orig_nvz, orig_nvr, orig_nvzeta, orig_nz, - r.n, nspecies) - for is ∈ 1:nspecies, iz ∈ 1:orig_nz, ivzeta ∈ 1:orig_nvzeta, - ivr ∈ 1:orig_nvr, ivz ∈ 1:orig_nvz - @views interpolate_to_grid_1d!( - new_pdf[ivz,ivr,ivzeta,iz,:,is], r.grid, - this_pdf[ivz,ivr,ivzeta,iz,:,is], restart_r, - restart_r_spectral) - end - this_pdf = new_pdf - end - if interpolation_needed["z"] - new_pdf = allocate_float(orig_nvz, orig_nvr, orig_nvzeta, z.n, - r.n, nspecies) - for is ∈ 1:nspecies, ir ∈ 1:r.n, ivzeta ∈ 1:orig_nvzeta, - ivr ∈ 1:orig_nvr, ivz ∈ 1:orig_nvz - @views interpolate_to_grid_1d!( - new_pdf[ivz,ivr,ivzeta,:,ir,is], z.grid, - this_pdf[ivz,ivr,ivzeta,:,ir,is], restart_z, - restart_z_spectral) - end - this_pdf = new_pdf - end - - # Current moment-kinetic implementation is only 1V, so no need - # to handle normalised vzeta or vr coordinates. This will need - # to change when/if 3V moment-kinetics is implemented. - if interpolation_needed["vzeta"] - new_pdf = allocate_float(orig_nvz, orig_nvr, vzeta.n, z.n, r.n, - nspecies) - for is ∈ 1:nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:orig_nvr, - ivz ∈ 1:orig_nvz - @views interpolate_to_grid_1d!( - new_pdf[ivz,ivr,:,iz,ir,is], vzeta.grid, - this_pdf[ivz,ivr,:,iz,ir,is], restart_vzeta, - restart_vzeta_spectral) - end - this_pdf = new_pdf - end - if interpolation_needed["vr"] - new_pdf = allocate_float(orig_nvz, vr.n, vzeta.n, z.n, r.n, - nspecies) - for is ∈ 1:nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, - ivzeta ∈ 1:vzeta.n, ivz ∈ 1:orig_nvz - @views interpolate_to_grid_1d!( - new_pdf[ivz,:,ivzeta,iz,ir,is], vr.grid, - this_pdf[ivz,:,ivzeta,iz,ir,is], restart_vr, - restart_vr_spectral) - end - this_pdf = new_pdf - end - - if ( - (moments.evolve_density == restart_evolve_density && - moments.evolve_upar == restart_evolve_upar && - moments.evolve_ppar == restart_evolve_ppar) - || (!moments.evolve_upar && !restart_evolve_upar && - !moments.evolve_ppar && !restart_evolve_ppar) - ) - # No chages to velocity-space coordinates, so just interpolate from - # one grid to the other - if interpolation_needed["vz"] - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) - for is ∈ 1:nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, - ivzeta ∈ 1:vzeta.n - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,ir,is], vz.grid, - this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - end - elseif (!moments.evolve_upar && !moments.evolve_ppar && - restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa = old_wpa + upar - # => old_wpa = new_wpa - upar - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivzeta ∈ 1:vzeta.n, - ivr ∈ 1:vr.n - restart_vz_vals = vz.grid .- moments.neutral.uz[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - elseif (!moments.evolve_upar && !moments.evolve_ppar && - !restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa = old_wpa*vth - # => old_wpa = new_wpa/vth - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, - ivzeta ∈ 1:vzeta.n - restart_vz_vals = vz.grid ./ moments.neutral.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - elseif (!moments.evolve_upar && !moments.evolve_ppar && - restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa = old_wpa*vth + upar - # => old_wpa = (new_wpa - upar)/vth - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, - ivzeta ∈ 1:vzeta.n - restart_vz_vals = - @. (vz.grid - moments.neutral.uz[iz,ir,is]) / - moments.neutral.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && !moments.evolve_ppar && - !restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa + upar = old_wpa - # => old_wpa = new_wpa + upar - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, - ivzeta ∈ 1:vzeta.n - restart_vz_vals = vz.grid .+ moments.neutral.uz[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, restart_vz_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && !moments.evolve_ppar && - !restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa + upar = old_wpa*vth - # => old_wpa = (new_wpa + upar)/vth - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, - ivzeta ∈ 1:vzeta.n - restart_vz_vals = - @. (vz.grid + moments.neutral.uz[iz,ir,is]) / - moments.neutral.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && !moments.evolve_ppar && - restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa + upar = old_wpa*vth + upar - # => old_wpa = new_wpa/vth - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, - ivzeta ∈ 1:vzeta.n - restart_vz_vals = vz.grid ./ moments.neutral.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - elseif (!moments.evolve_upar && moments.evolve_ppar && - !restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa*vth = old_wpa - # => old_wpa = new_wpa*vth - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, - ivzeta ∈ 1:vzeta.n - restart_vz_vals = vz.grid .* moments.neutral.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - elseif (!moments.evolve_upar && moments.evolve_ppar && - restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa*vth = old_wpa + upar - # => old_wpa = new_wpa*vth - upar/vth - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, - ivzeta ∈ 1:vzeta.n - restart_vz_vals = - @. vz.grid * moments.neutral.vth[iz,ir,is] - - moments.neutral.upar[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - elseif (!moments.evolve_upar && moments.evolve_ppar && - restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa*vth = old_wpa*vth + upar - # => old_wpa = new_wpa - upar/vth - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, - ivzeta ∈ 1:vzeta.n - restart_vz_vals = - @. vz.grid - - moments.neutral.uz[iz,ir,is]/moments.neutral.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && moments.evolve_ppar && - !restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa*vth + upar = old_wpa - # => old_wpa = new_wpa*vth + upar - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, - ivzeta ∈ 1:vzeta.n - restart_vz_vals = - @. vz.grid * moments.neutral.vth[iz,ir,is] + - moments.neutral.uz[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && moments.evolve_ppar && - restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa*vth + upar = old_wpa + upar - # => old_wpa = new_wpa*vth - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, - ivzeta ∈ 1:vzeta.n - restart_vz_vals = vz.grid .* moments.neutral.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && moments.evolve_ppar && - !restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa*vth + upar = old_wpa*vth - # => old_wpa = new_wpa + upar/vth - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, - ivzeta ∈ 1:vzeta.n - restart_vz_vals = - @. vz.grid + - moments.neutral.uz[iz,ir,is]/moments.neutral.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - else - # This should never happen, as all combinations of evolve_* options - # should be handled above. - error("Unsupported combination of moment-kinetic options:" - * " evolve_density=", moments.evolve_density - * " evolve_upar=", moments.evolve_upar - * " evolve_ppar=", moments.evolve_ppar - * " restart_evolve_density=", restart_evolve_density - * " restart_evolve_upar=", restart_evolve_upar - * " restart_evolve_ppar=", restart_evolve_ppar) - end - if moments.evolve_density && !restart_evolve_density - # Need to normalise by density - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n - this_pdf[:,:,:,iz,ir,is] ./= moments.neutral.dens[iz,ir,is] - end - elseif !moments.evolve_density && restart_evolve_density - # Need to unnormalise by density - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n - this_pdf[:,:,:,iz,ir,is] .*= moments.neutral.dens[iz,ir,is] - end - end - if moments.evolve_ppar && !restart_evolve_ppar - # Need to normalise by vth - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n - this_pdf[:,:,:,iz,ir,is] .*= moments.neutral.vth[iz,ir,is] - end - elseif !moments.evolve_ppar && restart_evolve_ppar - # Need to unnormalise by vth - for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n - this_pdf[:,:,:,iz,ir,is] ./= moments.neutral.vth[iz,ir,is] - end - end - - return this_pdf + reload_moment("external_source_neutral_controller_integral", + dynamic, time_index, r, z, r_range, z_range, + restart_r, restart_r_spectral, restart_z, + restart_z_spectral, interpolation_needed) end - pdf.neutral.norm .= load_neutral_pdf() - - function load_neutral_boundary_pdf(var_name, ir) - this_pdf = load_slice(boundary_distributions_io, var_name, vz_range, - vr_range, vzeta_range, z_range, :) - orig_nvz, orig_nvr, orig_nvzeta, orig_nz, nspecies = size(this_pdf) - if interpolation_needed["z"] - new_pdf = allocate_float(orig_nvz, orig_nvr, orig_nvzeta, z.n, - nspecies) - for is ∈ 1:nspecies, ivzeta ∈ 1:orig_nvzeta, ivr ∈ 1:orig_nvr, - ivz ∈ 1:orig_nvz - @views interpolate_to_grid_1d!( - new_pdf[ivz,ivr,ivzeta,:,is], z.grid, - this_pdf[ivz,ivr,ivzeta,:,is], restart_z, - restart_z_spectral) - end - this_pdf = new_pdf - end - - # Current moment-kinetic implementation is only 1V, so no need - # to handle normalised vzeta or vr coordinates. This will need - # to change when/if 3V moment-kinetics is implemented. - if interpolation_needed["vzeta"] - new_pdf = allocate_float(orig_nvz, orig_nvr, vzeta.n, z.n, - nspecies) - for is ∈ 1:nspecies, iz ∈ 1:z.n, ivr ∈ 1:orig_nvr, - ivz ∈ 1:orig_nvz - @views interpolate_to_grid_1d!( - new_pdf[ivz,ivr,:,iz,is], vzeta.grid, - this_pdf[ivz,ivr,:,iz,is], restart_vzeta, - restart_vzeta_spectral) - end - this_pdf = new_pdf - end - if interpolation_needed["vr"] - new_pdf = allocate_float(orig_nvz, vr.n, vzeta.n, z.n, nspecies) - for is ∈ 1:nspecies, iz ∈ 1:z.n, ivzeta ∈ 1:vzeta.n, - ivz ∈ 1:orig_nvz - @views interpolate_to_grid_1d!( - new_pdf[ivz,:,ivzeta,iz,is], vr.grid, - this_pdf[ivz,:,ivzeta,iz,is], restart_vr, - restart_vr_spectral) - end - this_pdf = new_pdf - end - - if ( - (moments.evolve_density == restart_evolve_density && - moments.evolve_upar == restart_evolve_upar && moments.evolve_ppar == - restart_evolve_ppar) - || (!moments.evolve_upar && !restart_evolve_upar && - !moments.evolve_ppar && !restart_evolve_ppar) - ) - # No chages to velocity-space coordinates, so just interpolate from - # one grid to the other - if interpolation_needed["vz"] - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) - for is ∈ 1:nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, - ivzeta ∈ 1:vzeta.n - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,is], vz.grid, - this_pdf[:,ivr,ivzeta,iz,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - end - elseif (!moments.evolve_upar && !moments.evolve_ppar && - restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa = old_wpa + upar - # => old_wpa = new_wpa - upar - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivzeta ∈ 1:vzeta.n, ivr ∈ 1:vr.n - restart_vz_vals = vz.grid .- moments.neutral.uz[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - elseif (!moments.evolve_upar && !moments.evolve_ppar && - !restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa = old_wpa*vth - # => old_wpa = new_wpa/vth - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, ivzeta ∈ 1:vzeta.n - restart_vz_vals = vz.grid ./ moments.neutral.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - elseif (!moments.evolve_upar && !moments.evolve_ppar && - restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa = old_wpa*vth + upar - # => old_wpa = (new_wpa - upar)/vth - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, ivzeta ∈ 1:vzeta.n - restart_vz_vals = - @. (vz.grid - moments.neutral.uz[iz,ir,is]) / - moments.neutral.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && !moments.evolve_ppar && - !restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa + upar = old_wpa - # => old_wpa = new_wpa + upar - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, ivzeta ∈ 1:vzeta.n - restart_vz_vals = vz.grid .+ - moments.neutral.uz[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && !moments.evolve_ppar && - !restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa + upar = old_wpa*vth - # => old_wpa = (new_wpa + upar)/vth - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, ivzeta ∈ 1:vzeta.n - restart_vz_vals = - @. (vz.grid + moments.neutral.uz[iz,ir,is]) / - moments.neutral.vth - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && !moments.evolve_ppar && - restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa + upar = old_wpa*vth + upar - # => old_wpa = new_wpa/vth - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, ivzeta ∈ 1:vzeta.n - restart_vz_vals = vz.grid ./ moments.neutral.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - elseif (!moments.evolve_upar && moments.evolve_ppar && - !restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa*vth = old_wpa - # => old_wpa = new_wpa*vth - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, ivzeta ∈ 1:vzeta.n - restart_vz_vals = vz.grid .* - moments.neutral.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - elseif (!moments.evolve_upar && moments.evolve_ppar && - restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa*vth = old_wpa + upar - # => old_wpa = new_wpa*vth - upar/vth - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, ivzeta ∈ 1:vzeta.n - restart_vz_vals = - @. vz.grid * moments.neutral.vth[iz,ir,is] - - moments.neutral.upar[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - elseif (!moments.evolve_upar && moments.evolve_ppar && - restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa*vth = old_wpa*vth + upar - # => old_wpa = new_wpa - upar/vth - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, ivzeta ∈ 1:vzeta.n - restart_vz_vals = - @. vz.grid - - moments.neutral.uz[iz,ir,is]/moments.neutral.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && moments.evolve_ppar && - !restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa*vth + upar = old_wpa - # => old_wpa = new_wpa*vth + upar - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, ivzeta ∈ 1:vzeta.n - restart_vz_vals = - @. vz.grid * moments.neutral.vth[iz,ir,is] + - moments.neutral.uz[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && moments.evolve_ppar && - restart_evolve_upar && !restart_evolve_ppar) - # vpa = new_wpa*vth + upar = old_wpa + upar - # => old_wpa = new_wpa*vth - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, ivzeta ∈ 1:vzeta.n - restart_vz_vals = vz.grid .* moments.neutral.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,is], restart_vz, - restart_vz_spectral) - end - this_pdf = new_pdf - elseif (moments.evolve_upar && moments.evolve_ppar && - !restart_evolve_upar && restart_evolve_ppar) - # vpa = new_wpa*vth + upar = old_wpa*vth - # => old_wpa = new_wpa + upar/vth - new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) - for is ∈ nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, ivzeta ∈ 1:vzeta.n - restart_vz_vals = - @. vz.grid + - moments.neutral.uz[iz,ir,is]/moments.neutral.vth[iz,ir,is] - @views interpolate_to_grid_1d!( - new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, - this_pdf[:,ivr,ivzeta,iz,is], restart_vz, - restart_vz_spectral) - end - else - # This should never happen, as all combinations of evolve_* options - # should be handled above. - error("Unsupported combination of moment-kinetic options:" - * " evolve_density=", moments.evolve_density - * " evolve_upar=", moments.evolve_upar - * " evolve_ppar=", moments.evolve_ppar - * " restart_evolve_density=", restart_evolve_density - * " restart_evolve_upar=", restart_evolve_upar - * " restart_evolve_ppar=", restart_evolve_ppar) - end - if moments.evolve_density && !restart_evolve_density - # Need to normalise by density - for is ∈ nspecies, iz ∈ 1:z.n - this_pdf[:,:,:,iz,is] ./= moments.neutral.dens[iz,ir,is] - end - elseif !moments.evolve_density && restart_evolve_density - # Need to unnormalise by density - for is ∈ nspecies, iz ∈ 1:z.n - this_pdf[:,:,:,iz,is] .*= moments.neutral.dens[iz,ir,is] - end - end - if moments.evolve_ppar && !restart_evolve_ppar - # Need to normalise by vth - for is ∈ nspecies, iz ∈ 1:z.n - this_pdf[:,:,:,iz,is] .*= moments.neutral.vth[iz,ir,is] - end - elseif !moments.evolve_ppar && restart_evolve_ppar - # Need to unnormalise by vth - for is ∈ nspecies, iz ∈ 1:z.n - this_pdf[:,:,:,iz,is] ./= moments.neutral.vth[iz,ir,is] - end - end - - return this_pdf - end + pdf.neutral.norm .= + reload_neutral_pdf(dynamic, time_index, moments, r, z, vzeta, vr, vz, + r_range, z_range, vzeta_range, vr_range, vz_range, + restart_r, restart_r_spectral, restart_z, + restart_z_spectral, restart_vzeta, + restart_vzeta_spectral, restart_vr, + restart_vr_spectral, restart_vz, + restart_vz_spectral, interpolation_needed, + restart_evolve_density, restart_evolve_upar, + restart_evolve_ppar) boundary_distributions.pdf_rboundary_neutral[:,:,:,:,1,:] .= - load_neutral_boundary_pdf("pdf_rboundary_neutral_left", 1) + reload_neutral_boundary_pdf(boundary_distributions_io, + "pdf_rboundary_neutral_left", 1, moments, + z, vzeta, vr, vz, z_range, vzeta_range, + vr_range, vz_range, restart_z, + restart_z_spectral, restart_vzeta, + restart_vzeta_spectral, restart_vr, + restart_vr_spectral, restart_vz, + restart_vz_spectral, interpolation_needed, + restart_evolve_density, + restart_evolve_upar, restart_evolve_ppar) boundary_distributions.pdf_rboundary_neutral[:,:,:,:,2,:] .= - load_neutral_boundary_pdf("pdf_rboundary_neutral_right", r.n) + reload_neutral_boundary_pdf(boundary_distributions_io, + "pdf_rboundary_neutral_right", r.n, + moments, z, vzeta, vr, vz, z_range, + vzeta_range, vr_range, vz_range, + restart_z, restart_z_spectral, + restart_vzeta, restart_vzeta_spectral, + restart_vr, restart_vr_spectral, + restart_vz, restart_vz_spectral, + interpolation_needed, + restart_evolve_density, + restart_evolve_upar, restart_evolve_ppar) end if "dt" ∈ keys(dynamic) @@ -1870,7 +858,7 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, # timestep, so just leave the value of "dt_before_last_fail" from # the input file. dt_before_last_fail = load_slice(dynamic, "dt_before_last_fail", - time_index) + time_index) end finally close(fid) @@ -1880,6 +868,1220 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, return code_time, dt, dt_before_last_fail, previous_runs_info, time_index end +function load_restart_coordinates(fid, r, z, vperp, vpa, vzeta, vr, vz, parallel_io) + if parallel_io + restart_z, restart_z_spectral, _ = + load_coordinate_data(fid, "z"; irank=z.irank, nrank=z.nrank) + restart_r, restart_r_spectral, _ = + load_coordinate_data(fid, "r"; irank=r.irank, nrank=r.nrank) + restart_vperp, restart_vperp_spectral, _ = + load_coordinate_data(fid, "vperp"; irank=vperp.irank, nrank=vperp.nrank) + restart_vpa, restart_vpa_spectral, _ = + load_coordinate_data(fid, "vpa"; irank=vpa.irank, nrank=vpa.nrank) + restart_vzeta, restart_vzeta_spectral, _ = + load_coordinate_data(fid, "vzeta"; irank=vzeta.irank, nrank=vzeta.nrank) + restart_vr, restart_vr_spectral, _ = + load_coordinate_data(fid, "vr"; irank=vr.irank, nrank=vr.nrank) + restart_vz, restart_vz_spectral, _ = + load_coordinate_data(fid, "vz"; irank=vz.irank, nrank=vz.nrank) + else + restart_z, restart_z_spectral, _ = load_coordinate_data(fid, "z") + restart_r, restart_r_spectral, _ = load_coordinate_data(fid, "r") + restart_vperp, restart_vperp_spectral, _ = + load_coordinate_data(fid, "vperp") + restart_vpa, restart_vpa_spectral, _ = load_coordinate_data(fid, "vpa") + restart_vzeta, restart_vzeta_spectral, _ = + load_coordinate_data(fid, "vzeta") + restart_vr, restart_vr_spectral, _ = load_coordinate_data(fid, "vr") + restart_vz, restart_vz_spectral, _ = load_coordinate_data(fid, "vz") + + if restart_r.nrank != r.nrank + error("Not using parallel I/O, and distributed MPI layout has " + * "changed: now r.nrank=$(r.nrank), but we are trying to " + * "restart from files ith restart_r.nrank=$(restart_r.nrank).") + end + if restart_z.nrank != z.nrank + error("Not using parallel I/O, and distributed MPI layout has " + * "changed: now z.nrank=$(z.nrank), but we are trying to " + * "restart from files ith restart_z.nrank=$(restart_z.nrank).") + end + if restart_vperp.nrank != vperp.nrank + error("Not using parallel I/O, and distributed MPI layout has " + * "changed: now vperp.nrank=$(vperp.nrank), but we are trying to " + * "restart from files ith restart_vperp.nrank=$(restart_vperp.nrank).") + end + if restart_vpa.nrank != vpa.nrank + error("Not using parallel I/O, and distributed MPI layout has " + * "changed: now vpa.nrank=$(vpa.nrank), but we are trying to " + * "restart from files ith restart_vpa.nrank=$(restart_vpa.nrank).") + end + if restart_vzeta.nrank != vzeta.nrank + error("Not using parallel I/O, and distributed MPI layout has " + * "changed: now vzeta.nrank=$(vzeta.nrank), but we are trying to " + * "restart from files ith restart_vzeta.nrank=$(restart_vzeta.nrank).") + end + if restart_vr.nrank != vr.nrank + error("Not using parallel I/O, and distributed MPI layout has " + * "changed: now vr.nrank=$(vr.nrank), but we are trying to " + * "restart from files ith restart_vr.nrank=$(restart_vr.nrank).") + end + if restart_vz.nrank != vz.nrank + error("Not using parallel I/O, and distributed MPI layout has " + * "changed: now vz.nrank=$(vz.nrank), but we are trying to " + * "restart from files ith restart_vz.nrank=$(restart_vz.nrank).") + end + end + + return restart_r, restart_r_spectral, restart_z, restart_z_spectral, restart_vperp, + restart_vperp_spectral, restart_vpa, restart_vpa_spectral, restart_vzeta, + restart_vzeta_spectral, restart_vr,restart_vr_spectral, restart_vz, + restart_vz_spectral +end + +function get_reload_ranges(parallel_io, restart_r, restart_z, restart_vperp, restart_vpa, + restart_vzeta, restart_vr, restart_vz) + if parallel_io + function get_range(coord) + if coord === nothing + return 1:0 + elseif coord.irank == coord.nrank - 1 + return coord.global_io_range + else + # Need to modify the range to load the end-point that is duplicated on + # the next process + this_range = coord.global_io_range + return this_range.start:(this_range.stop+1) + end + end + r_range = get_range(restart_r) + z_range = get_range(restart_z) + vperp_range = get_range(restart_vperp) + vpa_range = get_range(restart_vpa) + vzeta_range = get_range(restart_vzeta) + vr_range = get_range(restart_vr) + vz_range = get_range(restart_vz) + else + r_range = (:) + z_range = (:) + vperp_range = (:) + vpa_range = (:) + vzeta_range = (:) + vr_range = (:) + vz_range = (:) + end + return r_range, z_range, vperp_range, vpa_range, vzeta_range, vr_range, vz_range +end + +function reload_moment(var_name, dynamic, time_index, r, z, r_range, z_range, restart_r, + restart_r_spectral, restart_z, restart_z_spectral, + interpolation_needed) + moment = load_slice(dynamic, var_name, z_range, r_range, :, time_index) + orig_nz, orig_nr, nspecies = size(moment) + if interpolation_needed["r"] + new_moment = allocate_float(orig_nz, r.n, nspecies) + for is ∈ 1:nspecies, iz ∈ 1:orig_nz + @views interpolate_to_grid_1d!(new_moment[iz,:,is], r.grid, + moment[iz,:,is], restart_r, + restart_r_spectral) + end + moment = new_moment + end + if interpolation_needed["z"] + new_moment = allocate_float(z.n, r.n, nspecies) + for is ∈ 1:nspecies, ir ∈ 1:r.n + @views interpolate_to_grid_1d!(new_moment[:,ir,is], z.grid, + moment[:,ir,is], restart_z, + restart_z_spectral) + end + moment = new_moment + end + return moment +end + +function reload_ion_pdf(dynamic, time_index, moments, r, z, vperp, vpa, r_range, z_range, + vperp_range, vpa_range, restart_r, restart_r_spectral, restart_z, + restart_z_spectral, restart_vperp, restart_vperp_spectral, + restart_vpa, restart_vpa_spectral, interpolation_needed, + restart_evolve_density, restart_evolve_upar, restart_evolve_ppar) + + this_pdf = load_slice(dynamic, "f", vpa_range, vperp_range, z_range, + r_range, :, time_index) + orig_nvpa, orig_nvperp, orig_nz, orig_nr, nspecies = size(this_pdf) + if interpolation_needed["r"] + new_pdf = allocate_float(orig_nvpa, orig_nvperp, orig_nz, r.n, nspecies) + for is ∈ 1:nspecies, iz ∈ 1:orig_nz, ivperp ∈ 1:orig_nvperp, + ivpa ∈ 1:orig_nvpa + @views interpolate_to_grid_1d!( + new_pdf[ivpa,ivperp,iz,:,is], r.grid, + this_pdf[ivpa,ivperp,iz,:,is], restart_r, + restart_r_spectral) + end + this_pdf = new_pdf + end + if interpolation_needed["z"] + new_pdf = allocate_float(orig_nvpa, orig_nvperp, z.n, r.n, nspecies) + for is ∈ 1:nspecies, ir ∈ 1:r.n, ivperp ∈ 1:orig_nvperp, + ivpa ∈ 1:orig_nvpa + @views interpolate_to_grid_1d!( + new_pdf[ivpa,ivperp,:,ir,is], z.grid, + this_pdf[ivpa,ivperp,:,ir,is], restart_z, + restart_z_spectral) + end + this_pdf = new_pdf + end + + # Current moment-kinetic implementation is only 1V, so no need to handle a + # normalised vperp coordinate. This will need to change when 2V + # moment-kinetics is implemented. + if interpolation_needed["vperp"] + new_pdf = allocate_float(orig_nvpa, vperp.n, z.n, r.n, nspecies) + for is ∈ 1:nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivpa ∈ 1:orig_nvpa + @views interpolate_to_grid_1d!( + new_pdf[ivpa,:,iz,ir,is], vperp.grid, + this_pdf[ivpa,:,iz,ir,is], restart_vperp, + restart_vperp_spectral) + end + this_pdf = new_pdf + end + + if ( + (moments.evolve_density == restart_evolve_density && + moments.evolve_upar == restart_evolve_upar && moments.evolve_ppar == + restart_evolve_ppar) + || (!moments.evolve_upar && !restart_evolve_upar && + !moments.evolve_ppar && !restart_evolve_ppar) + ) + # No chages to velocity-space coordinates, so just interpolate from + # one grid to the other + if interpolation_needed["vpa"] + new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) + for is ∈ 1:nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,ir,is], vpa.grid, + this_pdf[:,ivperp,iz,ir,is], restart_vpa, + restart_vpa_spectral) + end + this_pdf = new_pdf + end + elseif (!moments.evolve_upar && !moments.evolve_ppar && + restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa = old_wpa + upar + # => old_wpa = new_wpa - upar + new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = vpa.grid .- moments.ion.upar[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,ir,is], restart_vpa, + restart_vpa_spectral) + end + this_pdf = new_pdf + elseif (!moments.evolve_upar && !moments.evolve_ppar && + !restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa = old_wpa*vth + # => old_wpa = new_wpa/vth + new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = vpa.grid ./ moments.ion.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,ir,is], restart_vpa, + restart_vpa_spectral) + end + this_pdf = new_pdf + elseif (!moments.evolve_upar && !moments.evolve_ppar && + restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa = old_wpa*vth + upar + # => old_wpa = (new_wpa - upar)/vth + new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = + @. (vpa.grid - moments.ion.upar[iz,ir,is]) / moments.ion.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,ir,is], restart_vpa, + restart_vpa_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && !moments.evolve_ppar && + !restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa + upar = old_wpa + # => old_wpa = new_wpa + upar + new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = vpa.grid .+ moments.ion.upar[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,ir,is], restart_vpa, + restart_vpa_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && !moments.evolve_ppar && + !restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa + upar = old_wpa*vth + # => old_wpa = (new_wpa + upar)/vth + new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = + @. (vpa.grid + moments.ion.upar[iz,ir,is]) / moments.ion.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,ir,is], restart_vpa, + restart_vpa_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && !moments.evolve_ppar && + restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa + upar = old_wpa*vth + upar + # => old_wpa = new_wpa/vth + new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = vpa.grid ./ moments.ion.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,ir,is], restart_vpa, + restart_vpa_spectral) + end + this_pdf = new_pdf + elseif (!moments.evolve_upar && moments.evolve_ppar && + !restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa*vth = old_wpa + # => old_wpa = new_wpa*vth + new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = vpa.grid .* moments.ion.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,ir,is], restart_vpa, + restart_vpa_spectral) + end + this_pdf = new_pdf + elseif (!moments.evolve_upar && moments.evolve_ppar && + restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa*vth = old_wpa + upar + # => old_wpa = new_wpa*vth - upar/vth + new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = @. vpa.grid * moments.ion.vth[iz,ir,is] - moments.ion.upar[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,ir,is], restart_vpa, + restart_vpa_spectral) + end + this_pdf = new_pdf + elseif (!moments.evolve_upar && moments.evolve_ppar && + restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa*vth = old_wpa*vth + upar + # => old_wpa = new_wpa - upar/vth + new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = + @. vpa.grid - moments.ion.upar[iz,ir,is]/moments.ion.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,ir,is], restart_vpa, + restart_vpa_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && moments.evolve_ppar && + !restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa*vth + upar = old_wpa + # => old_wpa = new_wpa*vth + upar + new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = @. vpa.grid * moments.ion.vth[iz,ir,is] + moments.ion.upar[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,ir,is], restart_vpa, + restart_vpa_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && moments.evolve_ppar && + restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa*vth + upar = old_wpa + upar + # => old_wpa = new_wpa*vth + new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = vpa.grid .* moments.ion.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,ir,is], restart_vpa, + restart_vpa_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && moments.evolve_ppar && + !restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa*vth + upar = old_wpa*vth + # => old_wpa = new_wpa + upar/vth + new_pdf = allocate_float(vpa.n, vperp.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = + @. vpa.grid + moments.ion.upar[iz,ir,is] / moments.ion.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,ir,is], restart_vpa, + restart_vpa_spectral) + end + this_pdf = new_pdf + else + # This should never happen, as all combinations of evolve_* options + # should be handled above. + error("Unsupported combination of moment-kinetic options:" + * " evolve_density=", moments.evolve_density + * " evolve_upar=", moments.evolve_upar + * " evolve_ppar=", moments.evolve_ppar + * " restart_evolve_density=", restart_evolve_density + * " restart_evolve_upar=", restart_evolve_upar + * " restart_evolve_ppar=", restart_evolve_ppar) + end + if moments.evolve_density && !restart_evolve_density + # Need to normalise by density + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n + this_pdf[:,:,iz,ir,is] ./= moments.ion.dens[iz,ir,is] + end + elseif !moments.evolve_density && restart_evolve_density + # Need to unnormalise by density + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n + this_pdf[:,:,iz,ir,is] .*= moments.ion.dens[iz,ir,is] + end + end + if moments.evolve_ppar && !restart_evolve_ppar + # Need to normalise by vth + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n + this_pdf[:,:,iz,ir,is] .*= moments.ion.vth[iz,ir,is] + end + elseif !moments.evolve_ppar && restart_evolve_ppar + # Need to unnormalise by vth + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n + this_pdf[:,:,iz,ir,is] ./= moments.ion.vth[iz,ir,is] + end + end + + return this_pdf +end + +function reload_ion_boundary_pdf(boundary_distributions_io, var_name, ir, moments, z, + vperp, vpa, z_range, vperp_range, vpa_range, restart_z, + restart_z_spectral, restart_vperp, + restart_vperp_spectral, restart_vpa, + restart_vpa_spectral, interpolation_needed, + restart_evolve_density, restart_evolve_upar, + restart_evolve_ppar) + this_pdf = load_slice(boundary_distributions_io, var_name, vpa_range, + vperp_range, z_range, :) + orig_nvpa, orig_nvperp, orig_nz, nspecies = size(this_pdf) + if interpolation_needed["z"] + new_pdf = allocate_float(orig_nvpa, orig_nvperp, z.n, nspecies) + for is ∈ 1:nspecies, ivperp ∈ 1:orig_nvperp, + ivpa ∈ 1:orig_nvpa + @views interpolate_to_grid_1d!( + new_pdf[ivpa,ivperp,:,is], z.grid, + this_pdf[ivpa,ivperp,:,is], restart_z, + restart_z_spectral) + end + this_pdf = new_pdf + end + + # Current moment-kinetic implementation is only 1V, so no need to handle a + # normalised vperp coordinate. This will need to change when 2V + # moment-kinetics is implemented. + if interpolation_needed["vperp"] + new_pdf = allocate_float(orig_nvpa, vperp.n, z.n, nspecies) + for is ∈ 1:nspecies, iz ∈ 1:z.n, ivpa ∈ 1:orig_nvpa + @views interpolate_to_grid_1d!( + new_pdf[ivpa,:,iz,is], vperp.grid, + this_pdf[ivpa,:,iz,is], restart_vperp, + restart_vperp_spectral) + end + this_pdf = new_pdf + end + + if ( + (moments.evolve_density == restart_evolve_density && + moments.evolve_upar == restart_evolve_upar && moments.evolve_ppar == + restart_evolve_ppar) + || (!moments.evolve_upar && !restart_evolve_upar && + !moments.evolve_ppar && !restart_evolve_ppar) + ) + # No chages to velocity-space coordinates, so just interpolate from + # one grid to the other + if interpolation_needed["vpa"] + new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) + for is ∈ 1:nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,is], vpa.grid, + this_pdf[:,ivperp,iz,is], restart_vpa, + restart_vpa_spectral) + end + this_pdf = new_pdf + end + elseif (!moments.evolve_upar && !moments.evolve_ppar && + restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa = old_wpa + upar + # => old_wpa = new_wpa - upar + new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = vpa.grid .- moments.ion.upar[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) + end + this_pdf = new_pdf + elseif (!moments.evolve_upar && !moments.evolve_ppar && + !restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa = old_wpa*vth + # => old_wpa = new_wpa/vth + new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = vpa.grid ./ moments.ion.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) + end + this_pdf = new_pdf + elseif (!moments.evolve_upar && !moments.evolve_ppar && + restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa = old_wpa*vth + upar + # => old_wpa = (new_wpa - upar)/vth + new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = + @. (vpa.grid - moments.ion.upar[iz,ir,is]) / + moments.ion.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && !moments.evolve_ppar && + !restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa + upar = old_wpa + # => old_wpa = new_wpa + upar + new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = vpa.grid .+ moments.ion.upar[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && !moments.evolve_ppar && + !restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa + upar = old_wpa*vth + # => old_wpa = (new_wpa + upar)/vth + new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = + @. (vpa.grid + moments.ion.upar[iz,ir,is]) / + moments.ion.vth + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && !moments.evolve_ppar && + restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa + upar = old_wpa*vth + upar + # => old_wpa = new_wpa/vth + new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = vpa.grid ./ moments.ion.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) + end + this_pdf = new_pdf + elseif (!moments.evolve_upar && moments.evolve_ppar && + !restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa*vth = old_wpa + # => old_wpa = new_wpa*vth + new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = vpa.grid .* moments.ion.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) + end + this_pdf = new_pdf + elseif (!moments.evolve_upar && moments.evolve_ppar && + restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa*vth = old_wpa + upar + # => old_wpa = new_wpa*vth - upar/vth + new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = @. vpa.grid * moments.ion.vth[iz,ir,is] - + moments.ion.upar[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) + end + this_pdf = new_pdf + elseif (!moments.evolve_upar && moments.evolve_ppar && + restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa*vth = old_wpa*vth + upar + # => old_wpa = new_wpa - upar/vth + new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = + @. vpa.grid - + moments.ion.upar[iz,ir,is]/moments.ion.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && moments.evolve_ppar && + !restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa*vth + upar = old_wpa + # => old_wpa = new_wpa*vth + upar + new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = @. vpa.grid * moments.ion.vth[iz,ir,is] + + moments.ion.upar[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && moments.evolve_ppar && + restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa*vth + upar = old_wpa + upar + # => old_wpa = new_wpa*vth + new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = vpa.grid .* moments.ion.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && moments.evolve_ppar && + !restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa*vth + upar = old_wpa*vth + # => old_wpa = new_wpa + upar/vth + new_pdf = allocate_float(vpa.n, vperp.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivperp ∈ 1:vperp.n + restart_vpa_vals = + @. vpa.grid + + moments.ion.upar[iz,ir,is] / moments.ion.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivperp,iz,is], restart_vpa_vals, + this_pdf[:,ivperp,iz,is], restart_vpa, restart_vpa_spectral) + end + this_pdf = new_pdf + else + # This should never happen, as all combinations of evolve_* options + # should be handled above. + error("Unsupported combination of moment-kinetic options:" + * " evolve_density=", moments.evolve_density + * " evolve_upar=", moments.evolve_upar + * " evolve_ppar=", moments.evolve_ppar + * " restart_evolve_density=", restart_evolve_density + * " restart_evolve_upar=", restart_evolve_upar + * " restart_evolve_ppar=", restart_evolve_ppar) + end + if moments.evolve_density && !restart_evolve_density + # Need to normalise by density + for is ∈ nspecies, iz ∈ 1:z.n + this_pdf[:,:,iz,is] ./= moments.ion.dens[iz,ir,is] + end + elseif !moments.evolve_density && restart_evolve_density + # Need to unnormalise by density + for is ∈ nspecies, iz ∈ 1:z.n + this_pdf[:,:,iz,is] .*= moments.ion.dens[iz,ir,is] + end + end + if moments.evolve_ppar && !restart_evolve_ppar + # Need to normalise by vth + for is ∈ nspecies, iz ∈ 1:z.n + this_pdf[:,:,iz,is] .*= moments.ion.vth[iz,ir,is] + end + elseif !moments.evolve_ppar && restart_evolve_ppar + # Need to unnormalise by vth + for is ∈ nspecies, iz ∈ 1:z.n + this_pdf[:,:,iz,is] ./= moments.ion.vth[iz,ir,is] + end + end + + return this_pdf +end + +function reload_neutral_pdf(dynamic, time_index, moments, r, z, vzeta, vr, vz, r_range, + z_range, vzeta_range, vr_range, vz_range, restart_r, + restart_r_spectral, restart_z, restart_z_spectral, + restart_vzeta, restart_vzeta_spectral, restart_vr, + restart_vr_spectral, restart_vz, restart_vz_spectral, + interpolation_needed, restart_evolve_density, + restart_evolve_upar, restart_evolve_ppar) + this_pdf = load_slice(dynamic, "f_neutral", vz_range, vr_range, + vzeta_range, z_range, r_range, :, time_index) + orig_nvz, orig_nvr, orig_nvzeta, orig_nz, orig_nr, nspecies = + size(this_pdf) + if interpolation_needed["r"] + new_pdf = allocate_float(orig_nvz, orig_nvr, orig_nvzeta, orig_nz, + r.n, nspecies) + for is ∈ 1:nspecies, iz ∈ 1:orig_nz, ivzeta ∈ 1:orig_nvzeta, + ivr ∈ 1:orig_nvr, ivz ∈ 1:orig_nvz + @views interpolate_to_grid_1d!( + new_pdf[ivz,ivr,ivzeta,iz,:,is], r.grid, + this_pdf[ivz,ivr,ivzeta,iz,:,is], restart_r, + restart_r_spectral) + end + this_pdf = new_pdf + end + if interpolation_needed["z"] + new_pdf = allocate_float(orig_nvz, orig_nvr, orig_nvzeta, z.n, + r.n, nspecies) + for is ∈ 1:nspecies, ir ∈ 1:r.n, ivzeta ∈ 1:orig_nvzeta, + ivr ∈ 1:orig_nvr, ivz ∈ 1:orig_nvz + @views interpolate_to_grid_1d!( + new_pdf[ivz,ivr,ivzeta,:,ir,is], z.grid, + this_pdf[ivz,ivr,ivzeta,:,ir,is], restart_z, + restart_z_spectral) + end + this_pdf = new_pdf + end + + # Current moment-kinetic implementation is only 1V, so no need + # to handle normalised vzeta or vr coordinates. This will need + # to change when/if 3V moment-kinetics is implemented. + if interpolation_needed["vzeta"] + new_pdf = allocate_float(orig_nvz, orig_nvr, vzeta.n, z.n, r.n, + nspecies) + for is ∈ 1:nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:orig_nvr, + ivz ∈ 1:orig_nvz + @views interpolate_to_grid_1d!( + new_pdf[ivz,ivr,:,iz,ir,is], vzeta.grid, + this_pdf[ivz,ivr,:,iz,ir,is], restart_vzeta, + restart_vzeta_spectral) + end + this_pdf = new_pdf + end + if interpolation_needed["vr"] + new_pdf = allocate_float(orig_nvz, vr.n, vzeta.n, z.n, r.n, + nspecies) + for is ∈ 1:nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, + ivzeta ∈ 1:vzeta.n, ivz ∈ 1:orig_nvz + @views interpolate_to_grid_1d!( + new_pdf[ivz,:,ivzeta,iz,ir,is], vr.grid, + this_pdf[ivz,:,ivzeta,iz,ir,is], restart_vr, + restart_vr_spectral) + end + this_pdf = new_pdf + end + + if ( + (moments.evolve_density == restart_evolve_density && + moments.evolve_upar == restart_evolve_upar && + moments.evolve_ppar == restart_evolve_ppar) + || (!moments.evolve_upar && !restart_evolve_upar && + !moments.evolve_ppar && !restart_evolve_ppar) + ) + # No chages to velocity-space coordinates, so just interpolate from + # one grid to the other + if interpolation_needed["vz"] + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) + for is ∈ 1:nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, + ivzeta ∈ 1:vzeta.n + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,ir,is], vz.grid, + this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + end + elseif (!moments.evolve_upar && !moments.evolve_ppar && + restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa = old_wpa + upar + # => old_wpa = new_wpa - upar + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivzeta ∈ 1:vzeta.n, + ivr ∈ 1:vr.n + restart_vz_vals = vz.grid .- moments.neutral.uz[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + elseif (!moments.evolve_upar && !moments.evolve_ppar && + !restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa = old_wpa*vth + # => old_wpa = new_wpa/vth + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, + ivzeta ∈ 1:vzeta.n + restart_vz_vals = vz.grid ./ moments.neutral.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + elseif (!moments.evolve_upar && !moments.evolve_ppar && + restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa = old_wpa*vth + upar + # => old_wpa = (new_wpa - upar)/vth + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, + ivzeta ∈ 1:vzeta.n + restart_vz_vals = + @. (vz.grid - moments.neutral.uz[iz,ir,is]) / + moments.neutral.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && !moments.evolve_ppar && + !restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa + upar = old_wpa + # => old_wpa = new_wpa + upar + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, + ivzeta ∈ 1:vzeta.n + restart_vz_vals = vz.grid .+ moments.neutral.uz[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, restart_vz_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && !moments.evolve_ppar && + !restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa + upar = old_wpa*vth + # => old_wpa = (new_wpa + upar)/vth + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, + ivzeta ∈ 1:vzeta.n + restart_vz_vals = + @. (vz.grid + moments.neutral.uz[iz,ir,is]) / + moments.neutral.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && !moments.evolve_ppar && + restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa + upar = old_wpa*vth + upar + # => old_wpa = new_wpa/vth + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, + ivzeta ∈ 1:vzeta.n + restart_vz_vals = vz.grid ./ moments.neutral.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + elseif (!moments.evolve_upar && moments.evolve_ppar && + !restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa*vth = old_wpa + # => old_wpa = new_wpa*vth + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, + ivzeta ∈ 1:vzeta.n + restart_vz_vals = vz.grid .* moments.neutral.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + elseif (!moments.evolve_upar && moments.evolve_ppar && + restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa*vth = old_wpa + upar + # => old_wpa = new_wpa*vth - upar/vth + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, + ivzeta ∈ 1:vzeta.n + restart_vz_vals = + @. vz.grid * moments.neutral.vth[iz,ir,is] - + moments.neutral.upar[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + elseif (!moments.evolve_upar && moments.evolve_ppar && + restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa*vth = old_wpa*vth + upar + # => old_wpa = new_wpa - upar/vth + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, + ivzeta ∈ 1:vzeta.n + restart_vz_vals = + @. vz.grid - + moments.neutral.uz[iz,ir,is]/moments.neutral.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && moments.evolve_ppar && + !restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa*vth + upar = old_wpa + # => old_wpa = new_wpa*vth + upar + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, + ivzeta ∈ 1:vzeta.n + restart_vz_vals = + @. vz.grid * moments.neutral.vth[iz,ir,is] + + moments.neutral.uz[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && moments.evolve_ppar && + restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa*vth + upar = old_wpa + upar + # => old_wpa = new_wpa*vth + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, + ivzeta ∈ 1:vzeta.n + restart_vz_vals = vz.grid .* moments.neutral.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && moments.evolve_ppar && + !restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa*vth + upar = old_wpa*vth + # => old_wpa = new_wpa + upar/vth + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, r.n, nspecies) + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n, ivr ∈ 1:vr.n, + ivzeta ∈ 1:vzeta.n + restart_vz_vals = + @. vz.grid + + moments.neutral.uz[iz,ir,is]/moments.neutral.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,ir,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + else + # This should never happen, as all combinations of evolve_* options + # should be handled above. + error("Unsupported combination of moment-kinetic options:" + * " evolve_density=", moments.evolve_density + * " evolve_upar=", moments.evolve_upar + * " evolve_ppar=", moments.evolve_ppar + * " restart_evolve_density=", restart_evolve_density + * " restart_evolve_upar=", restart_evolve_upar + * " restart_evolve_ppar=", restart_evolve_ppar) + end + if moments.evolve_density && !restart_evolve_density + # Need to normalise by density + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n + this_pdf[:,:,:,iz,ir,is] ./= moments.neutral.dens[iz,ir,is] + end + elseif !moments.evolve_density && restart_evolve_density + # Need to unnormalise by density + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n + this_pdf[:,:,:,iz,ir,is] .*= moments.neutral.dens[iz,ir,is] + end + end + if moments.evolve_ppar && !restart_evolve_ppar + # Need to normalise by vth + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n + this_pdf[:,:,:,iz,ir,is] .*= moments.neutral.vth[iz,ir,is] + end + elseif !moments.evolve_ppar && restart_evolve_ppar + # Need to unnormalise by vth + for is ∈ nspecies, ir ∈ 1:r.n, iz ∈ 1:z.n + this_pdf[:,:,:,iz,ir,is] ./= moments.neutral.vth[iz,ir,is] + end + end + + return this_pdf +end + +function reload_neutral_boundary_pdf(boundary_distributions_io, var_name, ir, moments, z, + vzeta, vr, vz, z_range, vzeta_range, vr_range, + vz_range, restart_z, restart_z_spectral, + restart_vzeta, restart_vzeta_spectral, restart_vr, + restart_vr_spectral, restart_vz, restart_vz_spectral, + interpolation_needed, restart_evolve_density, + restart_evolve_upar, restart_evolve_ppar) + this_pdf = load_slice(boundary_distributions_io, var_name, vz_range, + vr_range, vzeta_range, z_range, :) + orig_nvz, orig_nvr, orig_nvzeta, orig_nz, nspecies = size(this_pdf) + if interpolation_needed["z"] + new_pdf = allocate_float(orig_nvz, orig_nvr, orig_nvzeta, z.n, + nspecies) + for is ∈ 1:nspecies, ivzeta ∈ 1:orig_nvzeta, ivr ∈ 1:orig_nvr, + ivz ∈ 1:orig_nvz + @views interpolate_to_grid_1d!( + new_pdf[ivz,ivr,ivzeta,:,is], z.grid, + this_pdf[ivz,ivr,ivzeta,:,is], restart_z, + restart_z_spectral) + end + this_pdf = new_pdf + end + + # Current moment-kinetic implementation is only 1V, so no need + # to handle normalised vzeta or vr coordinates. This will need + # to change when/if 3V moment-kinetics is implemented. + if interpolation_needed["vzeta"] + new_pdf = allocate_float(orig_nvz, orig_nvr, vzeta.n, z.n, + nspecies) + for is ∈ 1:nspecies, iz ∈ 1:z.n, ivr ∈ 1:orig_nvr, + ivz ∈ 1:orig_nvz + @views interpolate_to_grid_1d!( + new_pdf[ivz,ivr,:,iz,is], vzeta.grid, + this_pdf[ivz,ivr,:,iz,is], restart_vzeta, + restart_vzeta_spectral) + end + this_pdf = new_pdf + end + if interpolation_needed["vr"] + new_pdf = allocate_float(orig_nvz, vr.n, vzeta.n, z.n, nspecies) + for is ∈ 1:nspecies, iz ∈ 1:z.n, ivzeta ∈ 1:vzeta.n, + ivz ∈ 1:orig_nvz + @views interpolate_to_grid_1d!( + new_pdf[ivz,:,ivzeta,iz,is], vr.grid, + this_pdf[ivz,:,ivzeta,iz,is], restart_vr, + restart_vr_spectral) + end + this_pdf = new_pdf + end + + if ( + (moments.evolve_density == restart_evolve_density && + moments.evolve_upar == restart_evolve_upar && moments.evolve_ppar == + restart_evolve_ppar) + || (!moments.evolve_upar && !restart_evolve_upar && + !moments.evolve_ppar && !restart_evolve_ppar) + ) + # No chages to velocity-space coordinates, so just interpolate from + # one grid to the other + if interpolation_needed["vz"] + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) + for is ∈ 1:nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, + ivzeta ∈ 1:vzeta.n + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,is], vz.grid, + this_pdf[:,ivr,ivzeta,iz,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + end + elseif (!moments.evolve_upar && !moments.evolve_ppar && + restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa = old_wpa + upar + # => old_wpa = new_wpa - upar + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivzeta ∈ 1:vzeta.n, ivr ∈ 1:vr.n + restart_vz_vals = vz.grid .- moments.neutral.uz[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + elseif (!moments.evolve_upar && !moments.evolve_ppar && + !restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa = old_wpa*vth + # => old_wpa = new_wpa/vth + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, ivzeta ∈ 1:vzeta.n + restart_vz_vals = vz.grid ./ moments.neutral.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + elseif (!moments.evolve_upar && !moments.evolve_ppar && + restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa = old_wpa*vth + upar + # => old_wpa = (new_wpa - upar)/vth + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, ivzeta ∈ 1:vzeta.n + restart_vz_vals = + @. (vz.grid - moments.neutral.uz[iz,ir,is]) / + moments.neutral.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && !moments.evolve_ppar && + !restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa + upar = old_wpa + # => old_wpa = new_wpa + upar + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, ivzeta ∈ 1:vzeta.n + restart_vz_vals = vz.grid .+ + moments.neutral.uz[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && !moments.evolve_ppar && + !restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa + upar = old_wpa*vth + # => old_wpa = (new_wpa + upar)/vth + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, ivzeta ∈ 1:vzeta.n + restart_vz_vals = + @. (vz.grid + moments.neutral.uz[iz,ir,is]) / + moments.neutral.vth + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && !moments.evolve_ppar && + restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa + upar = old_wpa*vth + upar + # => old_wpa = new_wpa/vth + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, ivzeta ∈ 1:vzeta.n + restart_vz_vals = vz.grid ./ moments.neutral.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + elseif (!moments.evolve_upar && moments.evolve_ppar && + !restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa*vth = old_wpa + # => old_wpa = new_wpa*vth + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, ivzeta ∈ 1:vzeta.n + restart_vz_vals = vz.grid .* + moments.neutral.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + elseif (!moments.evolve_upar && moments.evolve_ppar && + restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa*vth = old_wpa + upar + # => old_wpa = new_wpa*vth - upar/vth + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, ivzeta ∈ 1:vzeta.n + restart_vz_vals = + @. vz.grid * moments.neutral.vth[iz,ir,is] - + moments.neutral.upar[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + elseif (!moments.evolve_upar && moments.evolve_ppar && + restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa*vth = old_wpa*vth + upar + # => old_wpa = new_wpa - upar/vth + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, ivzeta ∈ 1:vzeta.n + restart_vz_vals = + @. vz.grid - + moments.neutral.uz[iz,ir,is]/moments.neutral.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && moments.evolve_ppar && + !restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa*vth + upar = old_wpa + # => old_wpa = new_wpa*vth + upar + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, ivzeta ∈ 1:vzeta.n + restart_vz_vals = + @. vz.grid * moments.neutral.vth[iz,ir,is] + + moments.neutral.uz[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && moments.evolve_ppar && + restart_evolve_upar && !restart_evolve_ppar) + # vpa = new_wpa*vth + upar = old_wpa + upar + # => old_wpa = new_wpa*vth + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, ivzeta ∈ 1:vzeta.n + restart_vz_vals = vz.grid .* moments.neutral.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,is], restart_vz, + restart_vz_spectral) + end + this_pdf = new_pdf + elseif (moments.evolve_upar && moments.evolve_ppar && + !restart_evolve_upar && restart_evolve_ppar) + # vpa = new_wpa*vth + upar = old_wpa*vth + # => old_wpa = new_wpa + upar/vth + new_pdf = allocate_float(vz.n, vr.n, vzeta.n, z.n, nspecies) + for is ∈ nspecies, iz ∈ 1:z.n, ivr ∈ 1:vr.n, ivzeta ∈ 1:vzeta.n + restart_vz_vals = + @. vz.grid + + moments.neutral.uz[iz,ir,is]/moments.neutral.vth[iz,ir,is] + @views interpolate_to_grid_1d!( + new_pdf[:,ivr,ivzeta,iz,is], restart_vz_vals, + this_pdf[:,ivr,ivzeta,iz,is], restart_vz, + restart_vz_spectral) + end + else + # This should never happen, as all combinations of evolve_* options + # should be handled above. + error("Unsupported combination of moment-kinetic options:" + * " evolve_density=", moments.evolve_density + * " evolve_upar=", moments.evolve_upar + * " evolve_ppar=", moments.evolve_ppar + * " restart_evolve_density=", restart_evolve_density + * " restart_evolve_upar=", restart_evolve_upar + * " restart_evolve_ppar=", restart_evolve_ppar) + end + if moments.evolve_density && !restart_evolve_density + # Need to normalise by density + for is ∈ nspecies, iz ∈ 1:z.n + this_pdf[:,:,:,iz,is] ./= moments.neutral.dens[iz,ir,is] + end + elseif !moments.evolve_density && restart_evolve_density + # Need to unnormalise by density + for is ∈ nspecies, iz ∈ 1:z.n + this_pdf[:,:,:,iz,is] .*= moments.neutral.dens[iz,ir,is] + end + end + if moments.evolve_ppar && !restart_evolve_ppar + # Need to normalise by vth + for is ∈ nspecies, iz ∈ 1:z.n + this_pdf[:,:,:,iz,is] .*= moments.neutral.vth[iz,ir,is] + end + elseif !moments.evolve_ppar && restart_evolve_ppar + # Need to unnormalise by vth + for is ∈ nspecies, iz ∈ 1:z.n + this_pdf[:,:,:,iz,is] ./= moments.neutral.vth[iz,ir,is] + end + end + + return this_pdf +end + """ Read a slice of an ion distribution function @@ -1888,11 +2090,11 @@ restarts (which are sequential in time), so concatenate the data from each entry The slice to take is specified by the keyword arguments. """ -function load_distributed_charged_pdf_slice(run_names::Tuple, nblocks::Tuple, t_range, - n_species::mk_int, r::coordinate, - z::coordinate, vperp::coordinate, - vpa::coordinate; is=nothing, ir=nothing, - iz=nothing, ivperp=nothing, ivpa=nothing) +function load_distributed_ion_pdf_slice(run_names::Tuple, nblocks::Tuple, t_range, + n_species::mk_int, r::coordinate, + z::coordinate, vperp::coordinate, + vpa::coordinate; is=nothing, ir=nothing, + iz=nothing, ivperp=nothing, ivpa=nothing) # dimension of pdf is [vpa,vperp,z,r,species,t] result_dims = mk_int[] @@ -2492,8 +2694,7 @@ function get_run_info_no_setup(run_dir::Union{AbstractString,Tuple{AbstractStrin load_coordinate_data(file_final_restart, "z") r_local, r_local_spectral, r_chunk_size = load_coordinate_data(file_final_restart, "r") - r, r_spectral, z, z_spectral = construct_global_zr_coords(r_local, z_local; - ignore_MPI=true) + r, r_spectral, z, z_spectral = construct_global_zr_coords(r_local, z_local) vperp, vperp_spectral, vperp_chunk_size = load_coordinate_data(file_final_restart, "vperp") @@ -2637,7 +2838,7 @@ function postproc_load_variable(run_info, variable_name; it=nothing, is=nothing, if ivperp === nothing if :vperp ∈ keys(run_info) # v-space coordinates only present if run_info contains distribution functions - nvperp = run_info.vperp.n + nvperp = run_info.vperp === nothing ? 1 : run_info.vperp.n ivperp = 1:nvperp else nvperp = nothing @@ -2651,7 +2852,7 @@ function postproc_load_variable(run_info, variable_name; it=nothing, is=nothing, if ivpa === nothing if :vpa ∈ keys(run_info) # v-space coordinates only present if run_info contains distribution functions - nvpa = run_info.vpa.n + nvpa = run_info.vpa === nothing ? 1 : run_info.vpa.n ivpa = 1:nvpa else nvpa = nothing @@ -2665,7 +2866,7 @@ function postproc_load_variable(run_info, variable_name; it=nothing, is=nothing, if ivzeta === nothing if :vzeta ∈ keys(run_info) # v-space coordinates only present if run_info contains distribution functions - nvzeta = run_info.vzeta.n + nvzeta = run_info.vzeta === nothing ? 1 : run_info.vzeta.n ivzeta = 1:nvzeta else nvzeta = nothing @@ -2679,7 +2880,7 @@ function postproc_load_variable(run_info, variable_name; it=nothing, is=nothing, if ivr === nothing if :vr ∈ keys(run_info) # v-space coordinates only present if run_info contains distribution functions - nvr = run_info.vr.n + nvr = run_info.vr === nothing ? 1 : run_info.vr.n ivr = 1:nvr else nvr = nothing @@ -2693,7 +2894,7 @@ function postproc_load_variable(run_info, variable_name; it=nothing, is=nothing, if ivz === nothing if :vz ∈ keys(run_info) # v-space coordinates only present if run_info contains distribution functions - nvz = run_info.vz.n + nvz = run_info.vz === nothing ? 1 : run_info.vz.n ivz = 1:nvz else nvz = nothing @@ -2913,14 +3114,13 @@ function postproc_load_variable(run_info, variable_name; it=nothing, is=nothing, run_info.r_local.n, run_info.itime_skip) result = result[iz,ir,is,it] elseif nd === 6 - result = load_distributed_charged_pdf_slice(run_info.files, run_info.nblocks, - it, run_info.n_ion_species, - run_info.r_local, - run_info.z_local, run_info.vperp, - run_info.vpa; - is=(is === (:) ? nothing : is), - ir=ir, iz=iz, ivperp=ivperp, - ivpa=ivpa) + result = load_distributed_ion_pdf_slice(run_info.files, run_info.nblocks, it, + run_info.n_ion_species, + run_info.r_local, run_info.z_local, + run_info.vperp, run_info.vpa; + is=(is === (:) ? nothing : is), + ir=ir, iz=iz, ivperp=ivperp, + ivpa=ivpa) elseif nd === 7 result = load_distributed_neutral_pdf_slice(run_info.files, run_info.nblocks, it, run_info.n_ion_species, @@ -3027,13 +3227,34 @@ function get_variable(run_info, variable_name; normalize_advection_speed_shape=t return variable end + # Get a 'per step' value from a saved 'cumulative' value. E.g. 'iterations per step' + # from a saved 'cumulative total iterations' + function get_per_step_from_cumulative_variable(run_info, varname::String; kwargs...) + variable = get_variable(run_info, varname; kwargs...) + tdim = ndims(variable) + for i ∈ size(variable, tdim):-1:2 + selectdim(variable, tdim, i) .-= selectdim(variable, tdim, i-1) + end + + # Per-step count does not make sense for the first step, so make sure element-1 is + # zero. + selectdim(variable, tdim, 1) .= zero(first(variable)) + + # Assume cumulative variables always increase, so if any value in the 'per-step' + # variable is negative, it is because there was a restart where the cumulative + # variable started over + variable .= max.(variable, zero(first(variable))) + + return variable + end + if variable_name == "temperature" vth = postproc_load_variable(run_info, "thermal_speed"; kwargs...) variable = vth.^2 - elseif variable_name == "collision_frequency" + elseif variable_name == "collision_frequency_ii" n = postproc_load_variable(run_info, "density"; kwargs...) vth = postproc_load_variable(run_info, "thermal_speed"; kwargs...) - variable = get_collision_frequency(run_info.collisions, n, vth) + variable = get_collision_frequency_ii(run_info.collisions, n, vth) elseif variable_name == "temperature_neutral" vth = postproc_load_variable(run_info, "thermal_speed_neutral"; kwargs...) variable = vth.^2 @@ -3126,8 +3347,14 @@ function get_variable(run_info, variable_name; normalize_advection_speed_shape=t dqpar_dz = get_z_derivative(run_info, "parallel_heat_flux") if run_info.external_source_settings.ion.active external_source_amplitude = get_variable(run_info, "external_source_amplitude") + external_source_density_amplitude = get_variable(run_info, "external_source_density_amplitude") + external_source_momentum_amplitude = get_variable(run_info, "external_source_momentum_amplitude") + external_source_pressure_amplitude = get_variable(run_info, "external_source_pressure_amplitude") else external_source_amplitude = zeros(0,0,run_info.nt) + external_source_density_amplitude = zeros(0,0,run_info.nt) + external_source_momentum_amplitude = zeros(0,0,run_info.nt) + external_source_pressure_amplitude = zeros(0,0,run_info.nt) end nz, nr, nspecies, nt = size(vth) @@ -3145,12 +3372,15 @@ function get_variable(run_info, variable_name; normalize_advection_speed_shape=t advect = [(speed=@view(speed[:,:,:,:,is,it]),) for is ∈ 1:nspecies] # Only need Ez fields = (Ez=@view(Ez[:,:,it]),) - @views moments = (charged=(dppar_dz=dppar_dz[:,:,:,it], - dupar_dz=dupar_dz[:,:,:,it], - dvth_dz=dvth_dz[:,:,:,it], - dqpar_dz=dqpar_dz[:,:,:,it], - vth=vth[:,:,:,it], - external_source_amplitude=external_source_amplitude[:,:,it]), + @views moments = (ion=(dppar_dz=dppar_dz[:,:,:,it], + dupar_dz=dupar_dz[:,:,:,it], + dvth_dz=dvth_dz[:,:,:,it], + dqpar_dz=dqpar_dz[:,:,:,it], + vth=vth[:,:,:,it], + external_source_amplitude=external_source_amplitude[:,:,it], + external_source_density_amplitude=external_source_density_amplitude[:,:,it], + external_source_momentum_amplitude=external_source_momentum_amplitude[:,:,it], + external_source_pressure_amplitude=external_source_pressure_amplitude[:,:,it]), evolve_density=run_info.evolve_density, evolve_upar=run_info.evolve_upar, evolve_ppar=run_info.evolve_ppar) @@ -3244,8 +3474,14 @@ function get_variable(run_info, variable_name; normalize_advection_speed_shape=t dqz_dz = get_z_derivative(run_info, "qz_neutral") if run_info.external_source_settings.neutral.active external_source_amplitude = get_variable(run_info, "external_source_neutral_amplitude") + external_source_density_amplitude = get_variable(run_info, "external_source_neutral_density_amplitude") + external_source_momentum_amplitude = get_variable(run_info, "external_source_neutral_momentum_amplitude") + external_source_pressure_amplitude = get_variable(run_info, "external_source_neutral_pressure_amplitude") else external_source_amplitude = zeros(0,0,run_info.nt) + external_source_density_amplitude = zeros(0,0,run_info.nt) + external_source_momentum_amplitude = zeros(0,0,run_info.nt) + external_source_pressure_amplitude = zeros(0,0,run_info.nt) end nz, nr, nspecies, nt = size(vth) @@ -3274,7 +3510,10 @@ function get_variable(run_info, variable_name; normalize_advection_speed_shape=t dvth_dz=dvth_dz[:,:,:,it], dqz_dz=dqz_dz[:,:,:,it], vth=vth[:,:,:,it], - external_source_amplitude=external_source_amplitude[:,:,it]), + external_source_amplitude=external_source_amplitude[:,:,it], + external_source_density_amplitude=external_source_density_amplitude[:,:,it], + external_source_momentum_amplitude=external_source_momentum_amplitude[:,:,it], + external_source_pressure_amplitude=external_source_pressure_amplitude[:,:,it]), evolve_density=run_info.evolve_density, evolve_upar=run_info.evolve_upar, evolve_ppar=run_info.evolve_ppar) @@ -3288,29 +3527,13 @@ function get_variable(run_info, variable_name; normalize_advection_speed_shape=t variable = speed variable = select_slice_of_variable(variable; kwargs...) elseif variable_name == "steps_per_output" - steps_per_output = get_variable(run_info, "step_counter"; kwargs...) - for i ∈ length(steps_per_output):-1:2 - steps_per_output[i] -= steps_per_output[i-1] - end - variable = steps_per_output + variable = get_per_step_from_cumulative_variable(run_info, "step_counter"; kwargs...) elseif variable_name == "failures_per_output" - failures_per_output = get_variable(run_info, "failure_counter"; kwargs...) - for i ∈ length(failures_per_output):-1:2 - failures_per_output[i] -= failures_per_output[i-1] - end - variable = failures_per_output + variable = get_per_step_from_cumulative_variable(run_info, "failure_counter"; kwargs...) elseif variable_name == "failure_caused_by_per_output" - failure_caused_by_per_output = get_variable(run_info, "failure_caused_by"; kwargs...) - for i ∈ size(failure_caused_by_per_output,2):-1:2 - failure_caused_by_per_output[:,i] .-= failure_caused_by_per_output[:,i-1] - end - variable = failure_caused_by_per_output + variable = get_per_step_from_cumulative_variable(run_info, "failure_caused_by"; kwargs...) elseif variable_name == "limit_caused_by_per_output" - limit_caused_by_per_output = get_variable(run_info, "limit_caused_by"; kwargs...) - for i ∈ size(limit_caused_by_per_output,2):-1:2 - limit_caused_by_per_output[:,i] .-= limit_caused_by_per_output[:,i-1] - end - variable = limit_caused_by_per_output + variable = get_per_step_from_cumulative_variable(run_info, "limit_caused_by"; kwargs...) elseif variable_name == "average_successful_dt" steps_per_output = get_variable(run_info, "steps_per_output"; kwargs...) failures_per_output = get_variable(run_info, "failures_per_output"; kwargs...) @@ -3322,6 +3545,11 @@ function get_variable(run_info, variable_name; normalize_advection_speed_shape=t end variable = delta_t ./ successful_steps_per_output + for i ∈ eachindex(successful_steps_per_output) + if successful_steps_per_output[i] == 0 + variable[i] = 0.0 + end + end if successful_steps_per_output[1] == 0 # Don't want a meaningless Inf... variable[1] = 0.0 diff --git a/moment_kinetics/src/moment_constraints.jl b/moment_kinetics/src/moment_constraints.jl index fbdf384a1..ae49821dd 100644 --- a/moment_kinetics/src/moment_constraints.jl +++ b/moment_kinetics/src/moment_constraints.jl @@ -6,7 +6,6 @@ function. module moment_constraints using ..communication: _block_synchronize -using ..initial_conditions: enforce_zero_incoming_bc! using ..looping using ..velocity_moments: integrate_over_vspace, update_qpar! @@ -69,12 +68,18 @@ function hard_force_moment_constraints!(f, moments, vpa) B = -A*I1/I2 @. f1d = A*f1d + B*vpa.grid*f1d + + C = NaN elseif moments.evolve_density I0 = integrate_over_vspace(f1d, vpa.wgts) - @. f1d = f1d / I0 + A = 1.0 / I0 + @. f1d = A * f1d + + B = NaN + C = NaN end - return nothing + return A, B, C end """ @@ -113,12 +118,18 @@ function hard_force_moment_constraints_neutral!(f, moments, vz) B = -A*I1/I2 @. f1d = A*f1d + B*vz.grid*f1d + + C = NaN elseif moments.evolve_density I0 = integrate_over_vspace(f1d, vz.wgts) - @. f1d = f1d / I0 + A = 1.0 / I0 + @. f1d = A * f1d + + B = NaN + C = NaN end - return nothing + return A, B, C end end diff --git a/moment_kinetics/src/moment_kinetics.jl b/moment_kinetics/src/moment_kinetics.jl index 5076e14ef..5e01d0885 100644 --- a/moment_kinetics/src/moment_kinetics.jl +++ b/moment_kinetics/src/moment_kinetics.jl @@ -41,7 +41,6 @@ include("em_fields.jl") include("bgk.jl") include("manufactured_solns.jl") # MRH Here? include("external_sources.jl") -include("initial_conditions.jl") include("moment_constraints.jl") include("fokker_planck_test.jl") include("fokker_planck_calculus.jl") @@ -54,6 +53,7 @@ include("vperp_advection.jl") include("neutral_r_advection.jl") include("neutral_z_advection.jl") include("neutral_vz_advection.jl") +include("boundary_conditions.jl") include("charge_exchange.jl") include("ionization.jl") include("krook_collisions.jl") @@ -65,8 +65,9 @@ include("numerical_dissipation.jl") include("moment_kinetics_input.jl") include("utils.jl") include("load_data.jl") -include("parameter_scans.jl") include("analysis.jl") +include("initial_conditions.jl") +include("parameter_scans.jl") include("time_advance.jl") using TimerOutputs @@ -76,15 +77,14 @@ using Primes using .file_io: setup_file_io, finish_file_io using .file_io: write_data_to_ascii -using .file_io: write_moments_data_to_binary, write_dfns_data_to_binary +using .file_io: write_all_moments_data_to_binary, write_all_dfns_data_to_binary using .command_line_options: get_options using .communication using .communication: _block_synchronize using .debugging using .external_sources using .input_structs -using .initial_conditions: allocate_pdf_and_moments, init_pdf_and_moments!, - enforce_boundary_conditions! +using .initial_conditions: allocate_pdf_and_moments, init_pdf_and_moments! using .load_data: reload_evolving_fields! using .looping using .moment_constraints: hard_force_moment_constraints! @@ -92,7 +92,11 @@ using .looping: debug_setup_loop_ranges_split_one_combination! using .moment_kinetics_input: mk_input, read_input_file using .time_advance: setup_time_advance!, time_advance! using .type_definitions: mk_int -using .utils: to_minutes +using .utils: to_minutes, get_default_restart_filename, + get_prefix_iblock_and_move_existing_file +using .em_fields: setup_em_fields +using .time_advance: setup_dummy_and_buffer_arrays +using .time_advance: allocate_advection_structs @debug_detect_redundant_block_synchronize using ..communication: debug_detect_redundant_is_active @@ -191,75 +195,6 @@ function run_moment_kinetics() restart_time_index=restart_time_index) end -""" -Append a number to the filename, to get a new, non-existing filename to backup the file -to. -""" -function get_backup_filename(filename) - if !isfile(filename) - error("Requested to restart from $filename, but this file does not exist") - end - counter = 1 - temp, extension = splitext(filename) - extension = extension[2:end] - temp, iblock_or_type = splitext(temp) - iblock_or_type = iblock_or_type[2:end] - iblock = nothing - basename = nothing - type = nothing - if iblock_or_type == "dfns" - iblock = nothing - type = iblock_or_type - basename = temp - parallel_io = true - else - # Filename had an iblock, so we are not using parallel I/O, but actually want to - # use the iblock for this block, not necessarily for the exact file that was - # passed. - iblock = iblock_index[] - basename, type = splitext(temp) - type = type[2:end] - parallel_io = false - end - if type != "dfns" - error("Must pass the '.dfns.h5' output file for restarting. Got $filename.") - end - backup_dfns_filename = "" - if parallel_io - # Using parallel I/O - while true - backup_dfns_filename = "$(basename)_$(counter).$(type).$(extension)" - if !isfile(backup_dfns_filename) - break - end - counter += 1 - end - # Create dfns_filename here even though it is the filename passed in, as - # parallel_io=false branch needs to get the right `iblock` for this block. - dfns_filename = "$(basename).dfns.$(extension)" - moments_filename = "$(basename).moments.$(extension)" - backup_moments_filename = "$(basename)_$(counter).moments.$(extension)" - else - while true - backup_dfns_filename = "$(basename)_$(counter).$(type).$(iblock).$(extension)" - if !isfile(backup_dfns_filename) - break - end - counter += 1 - end - # Create dfns_filename here even though it is almost the filename passed in, in - # order to get the right `iblock` for this block. - dfns_filename = "$(basename).dfns.$(iblock).$(extension)" - moments_filename = "$(basename).moments.$(iblock).$(extension)" - backup_moments_filename = "$(basename)_$(counter).moments.$(iblock).$(extension)" - end - backup_dfns_filename == "" && error("Failed to find a name for backup file.") - backup_prefix_iblock = ("$(basename)_$(counter)", iblock) - original_prefix_iblock = (basename, iblock) - return dfns_filename, backup_dfns_filename, parallel_io, moments_filename, - backup_moments_filename, backup_prefix_iblock, original_prefix_iblock -end - """ Perform all the initialization steps for a run. @@ -313,12 +248,28 @@ function setup_moment_kinetics(input_dict::AbstractDict; vperp=vperp.n, vpa=vpa.n, vzeta=vzeta.n, vr=vr.n, vz=vz.n) end + # create the "fields" structure that contains arrays + # for the electrostatic potential phi and the electromagnetic fields + fields = setup_em_fields(vperp.n, z.n, r.n, composition.n_ion_species, + drive_input.force_phi, drive_input.amplitude, + drive_input.frequency, drive_input.force_Er_zero_at_wall) + # Allocate arrays and create the pdf and moments structs pdf, moments, boundary_distributions = allocate_pdf_and_moments(composition, r, z, vperp, vpa, vzeta, vr, vz, evolve_moments, collisions, external_source_settings, num_diss_params) + # create structs containing the information needed to treat advection in z, r, vpa, vperp, and vz + # for ions, electrons and neutrals + # NB: the returned advection_structs are yet to be initialized + advection_structs = allocate_advection_structs(composition, z, r, vpa, vperp, vz, vr, vzeta) + + # setup dummy arrays & buffer arrays for z r MPI + n_neutral_species_alloc = max(1, composition.n_neutral_species) + scratch_dummy = setup_dummy_and_buffer_arrays(r.n, z.n, vpa.n, vperp.n, vz.n, vr.n, vzeta.n, + composition.n_ion_species, n_neutral_species_alloc) + if restart === false restarting = false # initialize f(z,vpa) and the lowest three v-space moments (density(z), upar(z) and ppar(z)), @@ -335,55 +286,14 @@ function setup_moment_kinetics(input_dict::AbstractDict; else restarting = true - run_name = input_dict["run_name"] - base_directory = get(input_dict, "base_directory", "runs") - output_dir = joinpath(base_directory, run_name) if restart === true - run_name = input_dict["run_name"] - io_settings = get(input_dict, "output", Dict{String,Any}()) - binary_format = get(io_settings, "binary_format", hdf5) - if binary_format === hdf5 - ext = "h5" - elseif binary_format === netcdf - ext = "cdf" - else - error("Unrecognized binary_format '$binary_format'") - end - restart_filename_pattern = joinpath(output_dir, run_name * ".dfns*." * ext) - restart_filename_glob = glob(restart_filename_pattern) - if length(restart_filename_glob) == 0 - error("No output file to restart from found matching the pattern " - * "$restart_filename_pattern") - end - restart_filename = restart_filename_glob[1] + restart_filename = get_default_restart_filename(io_input, "dfns") else restart_filename = restart end - # Move the output file being restarted from to make sure it doesn't get - # overwritten. - dfns_filename, backup_dfns_filename, parallel_io, moments_filename, - backup_moments_filename, backup_prefix_iblock, original_prefix_iblock = - get_backup_filename(restart_filename) - - # Ensure every process got the filenames and checked files exist before moving - # files - MPI.Barrier(comm_world) - - if abspath(output_dir) == abspath(dirname(dfns_filename)) - # Only move the file if it is in our current run directory. Otherwise we are - # restarting from another run, and will not be overwriting the file. - if (parallel_io && global_rank[] == 0) || (!parallel_io && block_rank[] == 0) - mv(dfns_filename, backup_dfns_filename) - mv(moments_filename, backup_moments_filename) - end - else - # Reload from dfns_filename without moving the file - backup_prefix_iblock = original_prefix_iblock - end - - # Ensure files have been moved before any process tries to read from them - MPI.Barrier(comm_world) + backup_prefix_iblock = get_prefix_iblock_and_move_existing_file(restart_filename, + io_input.output_dir) # Reload pdf and moments from an existing output file code_time, dt, dt_before_last_fail, previous_runs_info, restart_time_index = @@ -399,17 +309,22 @@ function setup_moment_kinetics(input_dict::AbstractDict; _block_synchronize() end + + # Broadcast code_time from the root process of each shared-memory block (on which it + # might have been loaded from a restart file). + code_time = MPI.Bcast(code_time, 0, comm_block[]) + # create arrays and do other work needed to setup # the main time advance loop -- including normalisation of f by density if requested - moments, fields, spectral_objects, advect_objects, - scratch, advance, t_params, fp_arrays, gyroavs, scratch_dummy, manufactured_source_list = - setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz_spectral, - vr_spectral, vzeta_spectral, vpa_spectral, vperp_spectral, z_spectral, - r_spectral, composition, drive_input, moments, t_input, code_time, dt, + moments, spectral_objects, scratch, advance, t_params, fp_arrays, gyroavs, + manufactured_source_list = + setup_time_advance!(pdf, fields, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, + vz_spectral, vr_spectral, vzeta_spectral, vpa_spectral, vperp_spectral, + z_spectral, r_spectral, composition, moments, t_input, code_time, dt, dt_before_last_fail, collisions, species, geometry, boundary_distributions, external_source_settings, num_diss_params, manufactured_solns_input, - restarting) + advection_structs, scratch_dummy, restarting) # This is the closest we can get to the end time of the setup before writing it to the # output file @@ -421,20 +336,21 @@ function setup_moment_kinetics(input_dict::AbstractDict; moments.evolve_upar, moments.evolve_ppar, external_source_settings, input_dict, restart_time_index, previous_runs_info, time_for_setup) # write initial data to ascii files - write_data_to_ascii(moments, fields, vpa, vperp, z, r, code_time, + write_data_to_ascii(pdf, moments, fields, vpa, vperp, z, r, code_time, composition.n_ion_species, composition.n_neutral_species, ascii_io) # write initial data to binary files - write_moments_data_to_binary(moments, fields, code_time, composition.n_ion_species, - composition.n_neutral_species, io_moments, 1, 0.0, t_params, r, z) - write_dfns_data_to_binary(pdf.charged.norm, pdf.neutral.norm, moments, fields, - code_time, composition.n_ion_species, composition.n_neutral_species, io_dfns, 1, - 0.0, t_params, r, z, vperp, vpa, vzeta, vr, vz) + write_all_moments_data_to_binary(moments, fields, code_time, + composition.n_ion_species, composition.n_neutral_species, io_moments, 1, 0.0, t_params, r, + z) + write_all_dfns_data_to_binary(pdf, moments, fields, code_time, + composition.n_ion_species, composition.n_neutral_species, io_dfns, 1, 0.0, + t_params, r, z, vperp, vpa, vzeta, vr, vz) begin_s_r_z_vperp_region() return pdf, scratch, code_time, t_params, vz, vr, vzeta, vpa, vperp, gyrophase, z, r, - moments, fields, spectral_objects, advect_objects, + moments, fields, spectral_objects, advection_structs, composition, collisions, geometry, gyroavs, boundary_distributions, external_source_settings, num_diss_params, advance, fp_arrays, scratch_dummy, manufactured_source_list, ascii_io, io_moments, io_dfns diff --git a/moment_kinetics/src/moment_kinetics_input.jl b/moment_kinetics/src/moment_kinetics_input.jl index 70e60fd96..56358a27b 100644 --- a/moment_kinetics/src/moment_kinetics_input.jl +++ b/moment_kinetics/src/moment_kinetics_input.jl @@ -25,6 +25,7 @@ using ..geo: init_magnetic_geometry, setup_geometry_input using MPI using Quadmath using TOML +using UUIDs """ Read input from a TOML file @@ -121,39 +122,39 @@ function mk_input(scan_input=Dict(); save_inputs_to_txt=false, ignore_MPI=true) geometry_in = setup_geometry_input(scan_input, get_default_rhostar(reference_params)) ispecies = 1 - species.charged[1].z_IC.initialization_option = get(scan_input, "z_IC_option$ispecies", "gaussian") - species.charged[1].initial_density = get(scan_input, "initial_density$ispecies", 1.0) - species.charged[1].initial_temperature = get(scan_input, "initial_temperature$ispecies", 1.0) - species.charged[1].z_IC.width = get(scan_input, "z_IC_width$ispecies", 0.125) - species.charged[1].z_IC.wavenumber = get(scan_input, "z_IC_wavenumber$ispecies", 1) - species.charged[1].z_IC.density_amplitude = get(scan_input, "z_IC_density_amplitude$ispecies", 0.001) - species.charged[1].z_IC.density_phase = get(scan_input, "z_IC_density_phase$ispecies", 0.0) - species.charged[1].z_IC.upar_amplitude = get(scan_input, "z_IC_upar_amplitude$ispecies", 0.0) - species.charged[1].z_IC.upar_phase = get(scan_input, "z_IC_upar_phase$ispecies", 0.0) - species.charged[1].z_IC.temperature_amplitude = get(scan_input, "z_IC_temperature_amplitude$ispecies", 0.0) - species.charged[1].z_IC.temperature_phase = get(scan_input, "z_IC_temperature_phase$ispecies", 0.0) - species.charged[1].r_IC.initialization_option = get(scan_input, "r_IC_option$ispecies", "gaussian") - species.charged[1].r_IC.wavenumber = get(scan_input, "r_IC_wavenumber$ispecies", 1) - species.charged[1].r_IC.density_amplitude = get(scan_input, "r_IC_density_amplitude$ispecies", 0.0) - species.charged[1].r_IC.density_phase = get(scan_input, "r_IC_density_phase$ispecies", 0.0) - species.charged[1].r_IC.upar_amplitude = get(scan_input, "r_IC_upar_amplitude$ispecies", 0.0) - species.charged[1].r_IC.upar_phase = get(scan_input, "r_IC_upar_phase$ispecies", 0.0) - species.charged[1].r_IC.temperature_amplitude = get(scan_input, "r_IC_temperature_amplitude$ispecies", 0.0) - species.charged[1].r_IC.temperature_phase = get(scan_input, "r_IC_temperature_phase$ispecies", 0.0) - species.charged[1].vpa_IC.initialization_option = get(scan_input, "vpa_IC_option$ispecies", "gaussian") - species.charged[1].vpa_IC.density_amplitude = get(scan_input, "vpa_IC_density_amplitude$ispecies", 1.000) - species.charged[1].vpa_IC.width = get(scan_input, "vpa_IC_width$ispecies", 1.0) - species.charged[1].vpa_IC.density_phase = get(scan_input, "vpa_IC_density_phase$ispecies", 0.0) - species.charged[1].vpa_IC.upar_amplitude = get(scan_input, "vpa_IC_upar_amplitude$ispecies", 0.0) - species.charged[1].vpa_IC.upar_phase = get(scan_input, "vpa_IC_upar_phase$ispecies", 0.0) - species.charged[1].vpa_IC.temperature_amplitude = get(scan_input, "vpa_IC_temperature_amplitude$ispecies", 0.0) - species.charged[1].vpa_IC.temperature_phase = get(scan_input, "vpa_IC_temperature_phase$ispecies", 0.0) + species.ion[1].z_IC.initialization_option = get(scan_input, "z_IC_option$ispecies", "gaussian") + species.ion[1].initial_density = get(scan_input, "initial_density$ispecies", 1.0) + species.ion[1].initial_temperature = get(scan_input, "initial_temperature$ispecies", 1.0) + species.ion[1].z_IC.width = get(scan_input, "z_IC_width$ispecies", 0.125) + species.ion[1].z_IC.wavenumber = get(scan_input, "z_IC_wavenumber$ispecies", 1) + species.ion[1].z_IC.density_amplitude = get(scan_input, "z_IC_density_amplitude$ispecies", 0.001) + species.ion[1].z_IC.density_phase = get(scan_input, "z_IC_density_phase$ispecies", 0.0) + species.ion[1].z_IC.upar_amplitude = get(scan_input, "z_IC_upar_amplitude$ispecies", 0.0) + species.ion[1].z_IC.upar_phase = get(scan_input, "z_IC_upar_phase$ispecies", 0.0) + species.ion[1].z_IC.temperature_amplitude = get(scan_input, "z_IC_temperature_amplitude$ispecies", 0.0) + species.ion[1].z_IC.temperature_phase = get(scan_input, "z_IC_temperature_phase$ispecies", 0.0) + species.ion[1].r_IC.initialization_option = get(scan_input, "r_IC_option$ispecies", "gaussian") + species.ion[1].r_IC.wavenumber = get(scan_input, "r_IC_wavenumber$ispecies", 1) + species.ion[1].r_IC.density_amplitude = get(scan_input, "r_IC_density_amplitude$ispecies", 0.0) + species.ion[1].r_IC.density_phase = get(scan_input, "r_IC_density_phase$ispecies", 0.0) + species.ion[1].r_IC.upar_amplitude = get(scan_input, "r_IC_upar_amplitude$ispecies", 0.0) + species.ion[1].r_IC.upar_phase = get(scan_input, "r_IC_upar_phase$ispecies", 0.0) + species.ion[1].r_IC.temperature_amplitude = get(scan_input, "r_IC_temperature_amplitude$ispecies", 0.0) + species.ion[1].r_IC.temperature_phase = get(scan_input, "r_IC_temperature_phase$ispecies", 0.0) + species.ion[1].vpa_IC.initialization_option = get(scan_input, "vpa_IC_option$ispecies", "gaussian") + species.ion[1].vpa_IC.density_amplitude = get(scan_input, "vpa_IC_density_amplitude$ispecies", 1.000) + species.ion[1].vpa_IC.width = get(scan_input, "vpa_IC_width$ispecies", 1.0) + species.ion[1].vpa_IC.density_phase = get(scan_input, "vpa_IC_density_phase$ispecies", 0.0) + species.ion[1].vpa_IC.upar_amplitude = get(scan_input, "vpa_IC_upar_amplitude$ispecies", 0.0) + species.ion[1].vpa_IC.upar_phase = get(scan_input, "vpa_IC_upar_phase$ispecies", 0.0) + species.ion[1].vpa_IC.temperature_amplitude = get(scan_input, "vpa_IC_temperature_amplitude$ispecies", 0.0) + species.ion[1].vpa_IC.temperature_phase = get(scan_input, "vpa_IC_temperature_phase$ispecies", 0.0) ispecies += 1 if n_neutral_species > 0 species.neutral[1].z_IC.initialization_option = get(scan_input, "z_IC_option$ispecies", "gaussian") species.neutral[1].initial_density = get(scan_input, "initial_density$ispecies", 1.0) species.neutral[1].initial_temperature = get(scan_input, "initial_temperature$ispecies", 1.0) - species.neutral[1].z_IC.width = get(scan_input, "z_IC_width$ispecies", species.charged[1].z_IC.width) + species.neutral[1].z_IC.width = get(scan_input, "z_IC_width$ispecies", species.ion[1].z_IC.width) species.neutral[1].z_IC.density_amplitude = get(scan_input, "z_IC_density_amplitude$ispecies", 0.001) species.neutral[1].z_IC.density_phase = get(scan_input, "z_IC_density_phase$ispecies", 0.0) species.neutral[1].z_IC.upar_amplitude = get(scan_input, "z_IC_upar_amplitude$ispecies", 0.0) @@ -169,7 +170,7 @@ function mk_input(scan_input=Dict(); save_inputs_to_txt=false, ignore_MPI=true) species.neutral[1].r_IC.temperature_phase = get(scan_input, "r_IC_temperature_phase$ispecies", 0.0) species.neutral[1].vpa_IC.initialization_option = get(scan_input, "vpa_IC_option$ispecies", "gaussian") species.neutral[1].vpa_IC.density_amplitude = get(scan_input, "vpa_IC_density_amplitude$ispecies", 1.000) - species.neutral[1].vpa_IC.width = get(scan_input, "vpa_IC_width$ispecies", species.charged[1].vpa_IC.width) + species.neutral[1].vpa_IC.width = get(scan_input, "vpa_IC_width$ispecies", species.ion[1].vpa_IC.width) species.neutral[1].vpa_IC.density_phase = get(scan_input, "vpa_IC_density_phase$ispecies", 0.0) species.neutral[1].vpa_IC.upar_amplitude = get(scan_input, "vpa_IC_upar_amplitude$ispecies", 0.0) species.neutral[1].vpa_IC.upar_phase = get(scan_input, "vpa_IC_upar_phase$ispecies", 0.0) @@ -179,7 +180,7 @@ function mk_input(scan_input=Dict(); save_inputs_to_txt=false, ignore_MPI=true) end #################### end specification of species inputs ##################### - charge_exchange = get(scan_input, "charge_exchange_frequency", 2.0*sqrt(species.charged[1].initial_temperature)) + charge_exchange = get(scan_input, "charge_exchange_frequency", 2.0*sqrt(species.ion[1].initial_temperature)) ionization = get(scan_input, "ionization_frequency", charge_exchange) constant_ionization_rate = get(scan_input, "constant_ionization_rate", false) # set up krook collision inputs @@ -192,7 +193,7 @@ function mk_input(scan_input=Dict(); save_inputs_to_txt=false, ignore_MPI=true) timestepping_section = set_defaults_and_check_section!( scan_input, "timestepping"; nstep=5, - dt=0.00025/sqrt(species.charged[1].initial_temperature), + dt=0.00025/sqrt(species.ion[1].initial_temperature), CFL_prefactor=-1.0, nwrite=1, nwrite_dfns=nothing, @@ -336,7 +337,7 @@ function mk_input(scan_input=Dict(); save_inputs_to_txt=false, ignore_MPI=true) # do not parallelise vpa with distributed-memory MPI vpa.nelement_local = vpa.nelement_global # L is the box length in units of vthermal_species - vpa.L = get(scan_input, "vpa_L", 8.0*sqrt(species.charged[1].initial_temperature)) + vpa.L = get(scan_input, "vpa_L", 8.0*sqrt(species.ion[1].initial_temperature)) # determine the boundary condition # only supported option at present is "zero" and "periodic" vpa.bc = get(scan_input, "vpa_bc", "periodic") @@ -354,7 +355,7 @@ function mk_input(scan_input=Dict(); save_inputs_to_txt=false, ignore_MPI=true) # do not parallelise vperp with distributed-memory MPI vperp.nelement_local = vperp.nelement_global # L is the box length in units of vthermal_species - vperp.L = get(scan_input, "vperp_L", 8.0*sqrt(species.charged[1].initial_temperature)) + vperp.L = get(scan_input, "vperp_L", 8.0*sqrt(species.ion[1].initial_temperature)) # Note vperp.bc is set below, after numerical dissipation is initialized, so that it # can use the numerical dissipation settings to set its default value. # @@ -398,7 +399,7 @@ function mk_input(scan_input=Dict(); save_inputs_to_txt=false, ignore_MPI=true) # do not parallelise vz with distributed-memory MPI vr.nelement_local = vr.nelement_global # L is the box length in units of vthermal_species - vr.L = get(scan_input, "vr_L", 8.0*sqrt(species.charged[1].initial_temperature)) + vr.L = get(scan_input, "vr_L", 8.0*sqrt(species.ion[1].initial_temperature)) # determine the boundary condition # only supported option at present is "zero" and "periodic" vr.bc = get(scan_input, "vr_bc", "none") @@ -415,7 +416,7 @@ function mk_input(scan_input=Dict(); save_inputs_to_txt=false, ignore_MPI=true) # do not parallelise vz with distributed-memory MPI vzeta.nelement_local = vzeta.nelement_global # L is the box length in units of vthermal_species - vzeta.L = get(scan_input, "vzeta_L", 8.0*sqrt(species.charged[1].initial_temperature)) + vzeta.L = get(scan_input, "vzeta_L", 8.0*sqrt(species.ion[1].initial_temperature)) # determine the boundary condition # only supported option at present is "zero" and "periodic" vzeta.bc = get(scan_input, "vzeta_bc", "none") @@ -427,14 +428,19 @@ function mk_input(scan_input=Dict(); save_inputs_to_txt=false, ignore_MPI=true) is_1V = (vperp.ngrid == vperp.nelement_global == 1 && vzeta.ngrid == vzeta.nelement_global == 1 && vr.ngrid == vr.nelement_global == 1) - num_diss_params = setup_numerical_dissipation( - get(scan_input, "numerical_dissipation", Dict{String,Any}()), is_1V) + + ion_num_diss_param_dict = get(scan_input, "ion_numerical_dissipation", Dict{String,Any}()) + electron_num_diss_param_dict = get(scan_input, "electron_numerical_dissipation", Dict{String,Any}()) + neutral_num_diss_param_dict = get(scan_input, "neutral_numerical_dissipation", Dict{String,Any}()) + num_diss_params = setup_numerical_dissipation(ion_num_diss_param_dict, + electron_num_diss_param_dict, + neutral_num_diss_param_dict, is_1V) # vperp.bc is set here (a bit out of place) so that we can use - # num_diss_params.vperp_dissipation_coefficient to set the default. + # num_diss_params.ion.vperp_dissipation_coefficient to set the default. vperp.bc = get(scan_input, "vperp_bc", ( collisions.fkpl.nuii > 0.0 || - num_diss_params.vperp_dissipation_coefficient > 0.0) ? + num_diss_params.ion.vperp_dissipation_coefficient > 0.0) ? "zero" : "none") ######################################################################### @@ -518,36 +524,36 @@ function mk_input(scan_input=Dict(); save_inputs_to_txt=false, ignore_MPI=true) vzeta_immutable = grid_input("vzeta", vzeta.ngrid, vzeta.nelement_global, vzeta.nelement_local, 1, 0, vzeta.L, vzeta.discretization, vzeta.fd_option, vzeta.cheb_option, vzeta.bc, vzeta_advection_immutable, MPI.COMM_NULL, vzeta.element_spacing_option) - species_charged_immutable = Array{species_parameters,1}(undef,n_ion_species) + species_ion_immutable = Array{species_parameters,1}(undef,n_ion_species) species_neutral_immutable = Array{species_parameters,1}(undef,n_neutral_species) for is ∈ 1:n_ion_species species_type = "ion" # species_type = "electron" - z_IC = initial_condition_input(species.charged[is].z_IC.initialization_option, - species.charged[is].z_IC.width, species.charged[is].z_IC.wavenumber, - species.charged[is].z_IC.density_amplitude, species.charged[is].z_IC.density_phase, - species.charged[is].z_IC.upar_amplitude, species.charged[is].z_IC.upar_phase, - species.charged[is].z_IC.temperature_amplitude, species.charged[is].z_IC.temperature_phase, - species.charged[is].z_IC.monomial_degree, 0.0, 0.0, 0.0, 0.0) - r_IC = initial_condition_input(species.charged[is].r_IC.initialization_option, - species.charged[is].r_IC.width, species.charged[is].r_IC.wavenumber, - species.charged[is].r_IC.density_amplitude, species.charged[is].r_IC.density_phase, - species.charged[is].r_IC.upar_amplitude, species.charged[is].r_IC.upar_phase, - species.charged[is].r_IC.temperature_amplitude, species.charged[is].r_IC.temperature_phase, - species.charged[is].r_IC.monomial_degree, 0.0, 0.0, 0.0, 0.0) - vpa_IC = initial_condition_input(species.charged[is].vpa_IC.initialization_option, - species.charged[is].vpa_IC.width, species.charged[is].vpa_IC.wavenumber, - species.charged[is].vpa_IC.density_amplitude, species.charged[is].vpa_IC.density_phase, - species.charged[is].vpa_IC.upar_amplitude, species.charged[is].vpa_IC.upar_phase, - species.charged[is].vpa_IC.temperature_amplitude, - species.charged[is].vpa_IC.temperature_phase, species.charged[is].vpa_IC.monomial_degree, + z_IC = initial_condition_input(species.ion[is].z_IC.initialization_option, + species.ion[is].z_IC.width, species.ion[is].z_IC.wavenumber, + species.ion[is].z_IC.density_amplitude, species.ion[is].z_IC.density_phase, + species.ion[is].z_IC.upar_amplitude, species.ion[is].z_IC.upar_phase, + species.ion[is].z_IC.temperature_amplitude, species.ion[is].z_IC.temperature_phase, + species.ion[is].z_IC.monomial_degree, 0.0, 0.0, 0.0, 0.0) + r_IC = initial_condition_input(species.ion[is].r_IC.initialization_option, + species.ion[is].r_IC.width, species.ion[is].r_IC.wavenumber, + species.ion[is].r_IC.density_amplitude, species.ion[is].r_IC.density_phase, + species.ion[is].r_IC.upar_amplitude, species.ion[is].r_IC.upar_phase, + species.ion[is].r_IC.temperature_amplitude, species.ion[is].r_IC.temperature_phase, + species.ion[is].r_IC.monomial_degree, 0.0, 0.0, 0.0, 0.0) + vpa_IC = initial_condition_input(species.ion[is].vpa_IC.initialization_option, + species.ion[is].vpa_IC.width, species.ion[is].vpa_IC.wavenumber, + species.ion[is].vpa_IC.density_amplitude, species.ion[is].vpa_IC.density_phase, + species.ion[is].vpa_IC.upar_amplitude, species.ion[is].vpa_IC.upar_phase, + species.ion[is].vpa_IC.temperature_amplitude, + species.ion[is].vpa_IC.temperature_phase, species.ion[is].vpa_IC.monomial_degree, get(scan_input, "vpa_IC_v0_$is", 0.5*sqrt(vperp.L^2 + (0.5*vpa.L)^2)), get(scan_input, "vpa_IC_vth0$is", 0.1*sqrt(vperp.L^2 + (0.5*vpa.L)^2)), get(scan_input, "vpa_IC_vpa0$is", 0.25*0.5*abs(vpa.L)), get(scan_input, "vpa_IC_vperp0$is", 0.5*abs(vperp.L))) - species_charged_immutable[is] = species_parameters(species_type, species.charged[is].initial_temperature, - species.charged[is].initial_density, z_IC, r_IC, vpa_IC) + species_ion_immutable[is] = species_parameters(species_type, species.ion[is].initial_temperature, + species.ion[is].initial_density, z_IC, r_IC, vpa_IC) end if n_neutral_species > 0 for is ∈ 1:n_neutral_species @@ -578,7 +584,7 @@ function mk_input(scan_input=Dict(); save_inputs_to_txt=false, ignore_MPI=true) species.neutral[is].initial_density, z_IC, r_IC, vpa_IC) end end - species_immutable = (charged = species_charged_immutable, neutral = species_neutral_immutable) + species_immutable = (ion = species_ion_immutable, neutral = species_neutral_immutable) force_Er_zero = get(scan_input, "force_Er_zero_at_wall", false) drive_immutable = drive_input(drive.force_phi, drive.amplitude, drive.frequency, force_Er_zero) @@ -590,6 +596,15 @@ function mk_input(scan_input=Dict(); save_inputs_to_txt=false, ignore_MPI=true) io_settings["binary_format"] = get(io_settings, "binary_format", hdf5) io_settings["parallel_io"] = get(io_settings, "parallel_io", io_has_parallel(Val(io_settings["binary_format"]))) + run_id = string(uuid4()) + if !ignore_MPI + # Communicate run_id to all blocks + # Need to convert run_id to a Vector{Char} for MPI + run_id_chars = [run_id...] + MPI.Bcast!(run_id_chars, 0, comm_world) + run_id = string(run_id_chars...) + end + io_settings["run_id"] = run_id io_immutable = io_input(; output_dir=output_dir, run_name=run_name, Dict(Symbol(k)=>v for (k,v) in io_settings)...) @@ -1013,7 +1028,7 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) electron_physics, use_test_neutral_wall_pdf, T_e, T_wall, phi_wall, Er_constant, mn_over_mi, me_over_mi, recycling_fraction, gyrokinetic_ions, allocate_float(n_species)) - species_charged = Array{species_parameters_mutable,1}(undef,n_ion_species) + species_ion = Array{species_parameters_mutable,1}(undef,n_ion_species) species_neutral = Array{species_parameters_mutable,1}(undef,n_neutral_species) # initial temperature for each species defaults to Tₑ @@ -1084,7 +1099,7 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) # fill in entries in species struct corresponding to ion species for is ∈ 1:n_ion_species - species_charged[is] = species_parameters_mutable("ion", initial_temperature, initial_density, + species_ion[is] = species_parameters_mutable("ion", initial_temperature, initial_density, deepcopy(z_initial_conditions), deepcopy(r_initial_conditions), deepcopy(vpa_initial_conditions)) end @@ -1096,7 +1111,7 @@ function load_defaults(n_ion_species, n_neutral_species, electron_physics) deepcopy(r_initial_conditions), deepcopy(vpa_initial_conditions)) end end - species = (charged = species_charged, neutral = species_neutral) + species = (ion = species_ion, neutral = species_neutral) # if drive_phi = true, include external electrostatic potential of form # phi(z,t=0)*drive_amplitude*sinpi(time*drive_frequency) diff --git a/moment_kinetics/src/moment_kinetics_structs.jl b/moment_kinetics/src/moment_kinetics_structs.jl index 25d23be1f..5ac80b23c 100644 --- a/moment_kinetics/src/moment_kinetics_structs.jl +++ b/moment_kinetics/src/moment_kinetics_structs.jl @@ -9,9 +9,9 @@ using ..type_definitions: mk_float """ """ -struct scratch_pdf{n_distribution_charged, n_moment, n_distribution_neutral,n_moment_neutral} - # charged particles - pdf::MPISharedArray{mk_float, n_distribution_charged} +struct scratch_pdf{n_distribution_ion, n_moment, n_distribution_neutral,n_moment_neutral} + # ions + pdf::MPISharedArray{mk_float, n_distribution_ion} density::MPISharedArray{mk_float, n_moment} upar::MPISharedArray{mk_float, n_moment} ppar::MPISharedArray{mk_float, n_moment} @@ -50,6 +50,235 @@ struct em_fields_struct force_Er_zero_at_wall::Bool end +""" +""" +struct moments_ion_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 perpendicular pressure + pperp::MPISharedArray{mk_float,3} + # 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 z-derivative of the particle density + ddens_dz::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} + # this is the entropy production dS/dt = - int (ln f sum_s' C_ss' [f_s,f_s']) d^3 v + dSdt::MPISharedArray{mk_float,3} + # Spatially varying amplitude of the external source term + external_source_amplitude::MPISharedArray{mk_float,2} + # Spatially varying amplitude of the density moment of the external source term + external_source_density_amplitude::MPISharedArray{mk_float,2} + # Spatially varying amplitude of the parallel momentum moment of the external source + # term + external_source_momentum_amplitude::MPISharedArray{mk_float,2} + # Spatially varying amplitude of the parallel pressure moment of the external source + # term + external_source_pressure_amplitude::MPISharedArray{mk_float,2} + # Integral term for the PID controller of the external source term + external_source_controller_integral::MPISharedArray{mk_float,2} + # Store coefficient 'A' from applying moment constraints so we can write it out as a + # diagnostic + constraints_A_coefficient::Union{MPISharedArray{mk_float,3},Nothing} + # Store coefficient 'B' from applying moment constraints so we can write it out as a + # diagnostic + constraints_B_coefficient::Union{MPISharedArray{mk_float,3},Nothing} + # Store coefficient 'C' from applying moment constraints so we can write it out as a + # diagnostic + constraints_C_coefficient::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::Union{MPISharedArray{mk_float,3},Nothing} + # 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} + # Spatially varying amplitude of the external source term + external_source_amplitude::MPISharedArray{mk_float,2} + # Spatially varying amplitude of the density moment of the external source term + external_source_density_amplitude::MPISharedArray{mk_float,2} + # Spatially varying amplitude of the parallel momentum moment of the external source + # term + external_source_momentum_amplitude::MPISharedArray{mk_float,2} + # Spatially varying amplitude of the parallel pressure moment of the external source + # term + external_source_pressure_amplitude::MPISharedArray{mk_float,2} + # Integral term for the PID controller of the external source term + external_source_controller_integral::MPISharedArray{mk_float,2} + # Store coefficient 'A' from applying moment constraints so we can write it out as a + # diagnostic + constraints_A_coefficient::Union{MPISharedArray{mk_float,3},Nothing} + # Store coefficient 'B' from applying moment constraints so we can write it out as a + # diagnostic + constraints_B_coefficient::Union{MPISharedArray{mk_float,3},Nothing} + # Store coefficient 'C' from applying moment constraints so we can write it out as a + # diagnostic + constraints_C_coefficient::Union{MPISharedArray{mk_float,3},Nothing} +end + +""" +""" +struct pdf_substruct{n_distribution} + norm::MPISharedArray{mk_float,n_distribution} + buffer::MPISharedArray{mk_float,n_distribution} # for collision operator terms when pdfs must be interpolated onto different velocity space grids, and for gyroaveraging +end + +# struct of structs neatly contains i+n info? +""" +""" +struct pdf_struct + #ion particles: s + r + z + vperp + vpa + ion::pdf_substruct{5} + #neutral particles: s + r + z + vzeta + vr + vz + neutral::pdf_substruct{6} +end + +""" +""" +struct moments_struct + ion::moments_ion_substruct + neutral::moments_neutral_substruct + # flag that indicates if the density should be evolved via continuity equation + evolve_density::Bool + # flag that indicates if particle number should be conserved for each species + # effects like ionisation or net particle flux from the domain would lead to + # non-conservation + particle_number_conserved::Bool + # flag that indicates if exact particle conservation should be enforced + enforce_conservation::Bool + # flag that indicates if the parallel flow should be evolved via force balance + evolve_upar::Bool + # flag that indicates if the parallel pressure should be evolved via the energy equation + evolve_ppar::Bool +end + +""" +""" +struct boundary_distributions_struct + # knudsen cosine distribution for imposing the neutral wall boundary condition + knudsen::MPISharedArray{mk_float,3} + # ion particle r boundary values (vpa,vperp,z,r,s) + pdf_rboundary_ion::MPISharedArray{mk_float,5} + # neutral particle r boundary values (vz,vr,vzeta,z,r,s) + pdf_rboundary_neutral::MPISharedArray{mk_float,6} +end + """ discretization_info for one dimension diff --git a/moment_kinetics/src/neutral_vz_advection.jl b/moment_kinetics/src/neutral_vz_advection.jl index 36ef2c2c9..96b61cd39 100644 --- a/moment_kinetics/src/neutral_vz_advection.jl +++ b/moment_kinetics/src/neutral_vz_advection.jl @@ -127,19 +127,21 @@ function update_speed_n_u_p_evolution_neutral!(advect, fvec, moments, vz, z, r, end end if neutral_source_settings.active - source_amplitude = moments.neutral.external_source_amplitude - source_T = neutral_source_settings.source_T + source_density_amplitude = moments.neutral.external_source_density_amplitude + source_momentum_amplitude = moments.neutral.external_source_momentum_amplitude + source_pressure_amplitude = moments.neutral.external_source_pressure_amplitude density = fvec.density_neutral uz = fvec.uz_neutral pz = fvec.pz_neutral vth = moments.neutral.vth vz_grid = vz.grid @loop_s_r_z is ir iz begin - prefactor = source_amplitude[iz,ir] - term1 = prefactor * uz[iz,ir,is]/(density[iz,ir,is]*vth[iz,ir,is]) + term1 = source_density_amplitude[iz,ir] * uz[iz,ir,is]/(density[iz,ir,is]*vth[iz,ir,is]) term2_over_vpa = - 0.5 * prefactor * (-(0.5*source_T + uz[iz,ir,is]^2) / pz[iz,ir,is] - + 1.0/density[iz,ir,is]) + -0.5 * (source_pressure_amplitude[iz,ir] + + 2.0 * uz[iz,ir,is] * source_momentum_amplitude[iz,ir]) / + pz[iz,ir,is] + + 0.5 * source_density_amplitude[iz,ir] / density[iz,ir,is] @loop_vzeta_vr_vz ivzeta ivr ivz begin advect[is].speed[ivz,ivr,ivzeta,iz,ir] += term1 + vz_grid[ivz] * term2_over_vpa @@ -218,13 +220,12 @@ function update_speed_n_u_evolution_neutral!(advect, fvec, moments, vz, z, r, co end end if neutral_source_settings.active - source_amplitude = moments.neutral.external_source_amplitude - source_T = neutral_source_settings.source_T + source_density_amplitude = moments.neutral.external_source_density_amplitude density = fvec.density_neutral uz = fvec.uz_neutral vth = moments.neutral.vth @loop_sn_r_z isn ir iz begin - term = source_amplitude[iz,ir] * uz[iz,ir,isn] / density[iz,ir,isn] + term = source_density_amplitude[iz,ir] * uz[iz,ir,isn] / density[iz,ir,isn] @loop_vzeta_vr_vz ivzeta ivr ivz begin advect[isn].speed[ivz,ivr,ivzeta,iz,ir] += term end diff --git a/moment_kinetics/src/numerical_dissipation.jl b/moment_kinetics/src/numerical_dissipation.jl index 64eb28138..054357c29 100644 --- a/moment_kinetics/src/numerical_dissipation.jl +++ b/moment_kinetics/src/numerical_dissipation.jl @@ -16,11 +16,36 @@ using ..calculus: derivative!, second_derivative! using ..derivatives: derivative_r!, derivative_z! using ..type_definitions: mk_float -Base.@kwdef struct numerical_dissipation_parameters + +############################################################# +########### Numerical Dissipation Parameter setup ########### +""" +Define the dissipation parameters for each species, which means +there need to be three sections in each input file that specify +the parameters required of each species, as follows: + +``` +[ion_numerical_dissipation] +vpa_dissipation_coefficient +... + +[electron_numerical_dissipation] +vpa_dissipation_coefficient +... + +[neutral_numerical_dissipation] +vz_dissipation_coefficient +... +``` + +There will still be the -1.0 default parameters. +""" + +# define individual structs for each species with their particular parameters +Base.@kwdef struct ion_num_diss_params vpa_boundary_buffer_damping_rate::mk_float = -1.0 vpa_boundary_buffer_diffusion_coefficient::mk_float = -1.0 vpa_dissipation_coefficient::mk_float = -1.0 - vz_dissipation_coefficient::mk_float = -1.0 vperp_dissipation_coefficient::mk_float = -1.0 z_dissipation_coefficient::mk_float = -1.0 r_dissipation_coefficient::mk_float = -1.0 @@ -28,18 +53,52 @@ Base.@kwdef struct numerical_dissipation_parameters force_minimum_pdf_value::Union{Nothing,mk_float} = nothing end -function setup_numerical_dissipation(input_section::Dict, is_1V) - if is_1V && "vpa_dissipation_coefficient" ∈ keys(input_section) +Base.@kwdef struct electron_num_diss_params + vpa_boundary_buffer_damping_rate::mk_float = -1.0 + vpa_boundary_buffer_diffusion_coefficient::mk_float = -1.0 + vpa_dissipation_coefficient::mk_float = -1.0 + vperp_dissipation_coefficient::mk_float = -1.0 + z_dissipation_coefficient::mk_float = -1.0 + r_dissipation_coefficient::mk_float = -1.0 + moment_dissipation_coefficient::mk_float = -1.0 + force_minimum_pdf_value::Union{Nothing,mk_float} = nothing +end + +Base.@kwdef struct neutral_num_diss_params + vz_dissipation_coefficient::mk_float = -1.0 + z_dissipation_coefficient::mk_float = -1.0 + r_dissipation_coefficient::mk_float = -1.0 + moment_dissipation_coefficient::mk_float = -1.0 + force_minimum_pdf_value::Union{Nothing,mk_float} = nothing +end + +struct numerical_dissipation_parameters + ion::ion_num_diss_params + electron::electron_num_diss_params + neutral::neutral_num_diss_params +end + +######### End Of Numerical Dissipation Parameter setup ######### +################################################################ + +function setup_numerical_dissipation(ion_input::Dict, electron_input::Dict, + neutral_input::Dict, is_1V) + if is_1V && "vpa_dissipation_coefficient" ∈ keys(ion_input) # Set default for vz_dissipation_coefficient the same as - # vpa_dissipation_coefficient for 1V case - input_section["vz_dissipation_coefficient"] = - get(input_section, "vz_dissipation_coefficient", - input_section["vpa_dissipation_coefficient"]) + # ion_vpa_dissipation_coefficient for 1V case + neutral_input["vz_dissipation_coefficient"] = + get(neutral_input, "vz_dissipation_coefficient", + ion_input["vpa_dissipation_coefficient"]) end - input = Dict(Symbol(k)=>v for (k,v) in input_section) + ion_input_dict = Dict(Symbol(k)=>v for (k,v) in ion_input) + ion_params = ion_num_diss_params(; ion_input_dict...) + electron_input_dict = Dict(Symbol(k)=>v for (k,v) in electron_input) + electron_params = electron_num_diss_params(; electron_input_dict...) + neutral_input_dict = Dict(Symbol(k)=>v for (k,v) in neutral_input) + neutral_params = neutral_num_diss_params(; neutral_input_dict...) - return numerical_dissipation_parameters(; input...) + return numerical_dissipation_parameters(ion_params, electron_params, neutral_params) end """ @@ -50,13 +109,11 @@ Disabled by default. The damping rate is set in the input TOML file by the parameter ``` -[numerical_dissipation] +[ion_numerical_dissipation] vpa_boundary_buffer_damping_rate = 0.1 ``` """ -function vpa_boundary_buffer_decay!(f_out, fvec_in, moments, vpa, dt, - num_diss_params::numerical_dissipation_parameters) - damping_rate_prefactor = num_diss_params.vpa_boundary_buffer_damping_rate +function vpa_boundary_buffer_decay!(f_out, fvec_in, moments, vpa, dt, damping_rate_prefactor) if damping_rate_prefactor <= 0.0 return nothing @@ -149,13 +206,11 @@ Disabled by default. The maximum diffusion rate in the buffer is set in the input TOML file by the parameter ``` -[numerical_dissipation] +[ion_numerical_dissipation] vpa_boundary_buffer_diffusion_coefficient = 0.1 ``` """ -function vpa_boundary_buffer_diffusion!(f_out, fvec_in, vpa, vpa_spectral, dt, - num_diss_params::numerical_dissipation_parameters) - diffusion_prefactor = num_diss_params.vpa_boundary_buffer_diffusion_coefficient +function vpa_boundary_buffer_diffusion!(f_out, fvec_in, vpa, vpa_spectral, dt, diffusion_prefactor) if diffusion_prefactor <= 0.0 return nothing @@ -238,14 +293,13 @@ Disabled by default. The diffusion coefficient is set in the input TOML file by the parameter ``` -[numerical_dissipation] +[ion_numerical_dissipation] vpa_dissipation_coefficient = 0.1 ``` """ function vpa_dissipation!(f_out, f_in, vpa, spectral::T_spectral, dt, - num_diss_params::numerical_dissipation_parameters) where T_spectral + diffusion_coefficient) where T_spectral - diffusion_coefficient = num_diss_params.vpa_dissipation_coefficient if diffusion_coefficient <= 0.0 || vpa.n == 1 return nothing end @@ -300,16 +354,15 @@ Disabled by default. The diffusion coefficient is set in the input TOML file by the parameter ``` -[numerical_dissipation] +[ion_numerical_dissipation] vperp_dissipation_coefficient = 0.1 ``` """ function vperp_dissipation!(f_out, f_in, vperp, spectral::T_spectral, dt, - num_diss_params::numerical_dissipation_parameters) where T_spectral + diffusion_coefficient) where T_spectral begin_s_r_z_vpa_region() - diffusion_coefficient = num_diss_params.vperp_dissipation_coefficient if diffusion_coefficient <= 0.0 || vperp.n == 1 return nothing end @@ -329,7 +382,7 @@ Disabled by default. The diffusion coefficient is set in the input TOML file by the parameter ``` -[numerical_dissipation] +[ion_numerical_dissipation] z_dissipation_coefficient = 0.1 ``` @@ -339,9 +392,8 @@ on internal or external element boundaries """ function z_dissipation!(f_out, f_in, z, z_spectral::T_spectral, dt, - num_diss_params::numerical_dissipation_parameters, scratch_dummy) where T_spectral + diffusion_coefficient, scratch_dummy) where T_spectral - diffusion_coefficient = num_diss_params.z_dissipation_coefficient if diffusion_coefficient <= 0.0 || z.n == 1 return nothing end @@ -374,7 +426,7 @@ Disabled by default. The diffusion coefficient is set in the input TOML file by the parameter ``` -[numerical_dissipation] +[ion_numerical_dissipation] r_dissipation_coefficient = 0.1 ``` @@ -385,9 +437,8 @@ on internal or external element boundaries """ function r_dissipation!(f_out, f_in, r, r_spectral::T_spectral, dt, - num_diss_params::numerical_dissipation_parameters, scratch_dummy) where T_spectral + diffusion_coefficient, scratch_dummy) where T_spectral - diffusion_coefficient = num_diss_params.r_dissipation_coefficient if diffusion_coefficient <= 0.0 || r.n == 1 return nothing end @@ -420,14 +471,13 @@ Disabled by default. The diffusion coefficient is set in the input TOML file by the parameter ``` -[numerical_dissipation] +[neutral_numerical_dissipation] vz_dissipation_coefficient = 0.1 ``` """ function vz_dissipation_neutral!(f_out, f_in, vz, spectral::T_spectral, dt, - num_diss_params::numerical_dissipation_parameters) where T_spectral + diffusion_coefficient) where T_spectral - diffusion_coefficient = num_diss_params.vz_dissipation_coefficient if diffusion_coefficient <= 0.0 return nothing end @@ -449,7 +499,7 @@ Disabled by default. The diffusion coefficient is set in the input TOML file by the parameter ``` -[numerical_dissipation] +[neutral_numerical_dissipation] z_dissipation_coefficient = 0.1 ``` @@ -459,9 +509,8 @@ on internal or external element boundaries """ function z_dissipation_neutral!(f_out, f_in, z, z_spectral::T_spectral, dt, - num_diss_params::numerical_dissipation_parameters, scratch_dummy) where T_spectral + diffusion_coefficient, scratch_dummy) where T_spectral - diffusion_coefficient = num_diss_params.z_dissipation_coefficient if diffusion_coefficient <= 0.0 return nothing end @@ -494,7 +543,7 @@ Disabled by default. The diffusion coefficient is set in the input TOML file by the parameter ``` -[numerical_dissipation] +[neutral_numerical_dissipation] r_dissipation_coefficient = 0.1 ``` @@ -505,9 +554,8 @@ on internal or external element boundaries """ function r_dissipation_neutral!(f_out, f_in, r, r_spectral::T_spectral, dt, - num_diss_params::numerical_dissipation_parameters, scratch_dummy) where T_spectral + diffusion_coefficient, scratch_dummy) where T_spectral - diffusion_coefficient = num_diss_params.r_dissipation_coefficient if diffusion_coefficient <= 0.0 || r.n == 1 return nothing end @@ -534,17 +582,16 @@ function r_dissipation_neutral!(f_out, f_in, r, r_spectral::T_spectral, dt, end """ - force_minimum_pdf_value!(f, num_diss_paras::numerical_dissipation_parameters) + force_minimum_pdf_value!(f, minval) Set a minimum value for the pdf-sized array `f`. Any points less than the minimum are set to the minimum. By default, no minimum is applied. The minimum value can be set by ``` -[numerical_dissipation] +[ion_numerical_dissipation] force_minimum_pdf_value = 0.0 ``` """ -function force_minimum_pdf_value!(f, num_diss_params::numerical_dissipation_parameters) - minval = num_diss_params.force_minimum_pdf_value +function force_minimum_pdf_value!(f, minval) if minval === nothing return nothing @@ -560,17 +607,16 @@ function force_minimum_pdf_value!(f, num_diss_params::numerical_dissipation_para end """ - force_minimum_pdf_value_neutral!(f, num_diss_paras::numerical_dissipation_parameters) + force_minimum_pdf_value_neutral!(f, minval) Set a minimum value for the neutral-pdf-sized array `f`. Any points less than the minimum are set to the minimum. By default, no minimum is applied. The minimum value can be set by ``` -[numerical_dissipation] +[neutral_numerical_dissipation] force_minimum_pdf_value = 0.0 ``` """ -function force_minimum_pdf_value_neutral!(f, num_diss_params::numerical_dissipation_parameters) - minval = num_diss_params.force_minimum_pdf_value +function force_minimum_pdf_value_neutral!(f, minval) if minval === nothing return nothing diff --git a/moment_kinetics/src/r_advection.jl b/moment_kinetics/src/r_advection.jl index e3f23aef7..3d7d3a0a4 100644 --- a/moment_kinetics/src/r_advection.jl +++ b/moment_kinetics/src/r_advection.jl @@ -21,7 +21,7 @@ function r_advection!(f_out, fvec_in, moments, fields, advect, r, z, vperp, vpa, @loop_s is begin # get the updated speed along the r direction using the current f @views update_speed_r!(advect[is], fvec_in.upar[:,:,is], - moments.charged.vth[:,:,is], fields, moments.evolve_upar, + moments.ion.vth[:,:,is], fields, moments.evolve_upar, moments.evolve_ppar, vpa, vperp, z, r, geometry, is) # advance r-advection equation @loop_z_vpa iz ivpa begin @@ -30,14 +30,14 @@ function r_advection!(f_out, fvec_in, moments, fields, advect, r, z, vperp, vpa, @loop_z_vperp_vpa iz ivperp ivpa begin @views adjust_advection_speed!(advect[is].speed[:,ivpa,ivperp,iz], fvec_in.density[iz,:,is], - moments.charged.vth[iz,:,is], + moments.ion.vth[iz,:,is], moments.evolve_density, moments.evolve_ppar) # take the normalized pdf contained in fvec_in.pdf and remove the normalization, # returning the true (un-normalized) particle distribution function in r.scratch @views unnormalize_pdf!( scratch_dummy.buffer_vpavperpzrs_2[ivpa,ivperp,iz,:,is], fvec_in.pdf[ivpa,ivperp,iz,:,is], fvec_in.density[iz,:,is], - moments.charged.vth[iz,:,is], moments.evolve_density, + moments.ion.vth[iz,:,is], moments.evolve_density, moments.evolve_ppar) advect[is].adv_fac[:,ivpa,ivperp,iz] .= -dt.*advect[is].speed[:,ivpa,ivperp,iz] end diff --git a/moment_kinetics/src/reference_parameters.jl b/moment_kinetics/src/reference_parameters.jl index 7ccddb581..869451cc4 100644 --- a/moment_kinetics/src/reference_parameters.jl +++ b/moment_kinetics/src/reference_parameters.jl @@ -8,7 +8,7 @@ physical units of the simulation, and are needed for a few specific steps during module reference_parameters export setup_reference_parameters -export get_reference_collision_frequency +export get_reference_collision_frequency_ii using ..constants using ..input_structs @@ -29,28 +29,33 @@ function setup_reference_parameters(input_dict) reference_parameter_section["timeref"] = reference_parameter_section["Lref"] / reference_parameter_section["cref"] reference_parameter_section["Omegaref"] = proton_charge * reference_parameter_section["Bref"] / reference_parameter_section["mref"] + Nref_per_cm3 = reference_parameter_section["Nref"] * 1.0e-6 + Tref = reference_parameter_section["Tref"] + + reference_parameter_section["me"] = electron_mass + + # Coulomb logarithm at reference parameters for same-species, singly-charged ion-ion + # collisions, using NRL formulary. Formula given for n in units of cm^-3 and T in + # units of eV. + reference_parameter_section["logLambda_ii"] = 23.0 - log(sqrt(2.0*Nref_per_cm3) / Tref^1.5) + reference_params = Dict_to_NamedTuple(reference_parameter_section) return reference_params end """ -Calculate normalized collision frequency at reference parameters for Coulomb collisions. +Calculate normalized ion-ion collision frequency at reference parameters for Coulomb collisions. Currently valid only for hydrogenic ions (Z=1) """ -function get_reference_collision_frequency(reference_params) +function get_reference_collision_frequency_ii(reference_params) Nref = reference_params.Nref Tref = reference_params.Tref mref = reference_params.mref timeref = reference_params.timeref cref = reference_params.cref - - Nref_per_cm3 = Nref * 1.0e-6 - - # Coulomb logarithm at reference parameters for same-species ion-ion collisions, using - # NRL formulary. Formula given for n in units of cm^-3 and T in units of eV. - logLambda_ii = 23.0 - log(sqrt(2.0*Nref_per_cm3) / Tref^1.5) + logLambda_ii = reference_params.logLambda_ii # Collision frequency, using \hat{\nu} from Appendix, p. 277 of Helander "Collisional # Transport in Magnetized Plasmas" (2002). diff --git a/moment_kinetics/src/source_terms.jl b/moment_kinetics/src/source_terms.jl index 53dd88b26..9850d6f23 100644 --- a/moment_kinetics/src/source_terms.jl +++ b/moment_kinetics/src/source_terms.jl @@ -22,9 +22,9 @@ function source_terms!(pdf_out, fvec_in, moments, vpa, z, r, dt, spectral, compo @loop_s is begin @views source_terms_evolve_ppar_no_collisions!( pdf_out[:,:,:,:,is], fvec_in.pdf[:,:,:,:,is], fvec_in.density[:,:,is], - fvec_in.upar[:,:,is], fvec_in.ppar[:,:,is], moments.charged.vth[:,:,is], - moments.charged.qpar[:,:,is], moments.charged.ddens_dz[:,:,is], - moments.charged.dvth_dz[:,:,is], moments.charged.dqpar_dz[:,:,is], + fvec_in.upar[:,:,is], fvec_in.ppar[:,:,is], moments.ion.vth[:,:,is], + moments.ion.qpar[:,:,is], moments.ion.ddens_dz[:,:,is], + moments.ion.dvth_dz[:,:,is], moments.ion.dqpar_dz[:,:,is], moments, z, r, dt, spectral, ion_source_settings) if composition.n_neutral_species > 0 if abs(collisions.charge_exchange) > 0.0 || abs(collisions.ionization) > 0.0 @@ -41,8 +41,8 @@ function source_terms!(pdf_out, fvec_in, moments, vpa, z, r, dt, spectral, compo @loop_s is begin @views source_terms_evolve_density!( pdf_out[:,:,:,:,is], fvec_in.pdf[:,:,:,:,is], fvec_in.density[:,:,is], - fvec_in.upar[:,:,is], moments.charged.ddens_dz[:,:,is], - moments.charged.dupar_dz[:,:,is], moments, z, r, dt, spectral, + fvec_in.upar[:,:,is], moments.ion.ddens_dz[:,:,is], + moments.ion.dupar_dz[:,:,is], moments, z, r, dt, spectral, ion_source_settings) end end @@ -65,9 +65,9 @@ function source_terms_evolve_density!(pdf_out, pdf_in, dens, upar, ddens_dz, dup end if ion_source_settings.active - source_amplitude = moments.charged.external_source_amplitude + source_density_amplitude = moments.ion.external_source_density_amplitude @loop_r_z ir iz begin - term = dt * source_amplitude[iz,ir] / dens[iz,ir] + term = dt * source_density_amplitude[iz,ir] / dens[iz,ir] @loop_vperp_vpa ivperp ivpa begin pdf_out[ivpa,ivperp,iz,ir] -= term * pdf_in[ivpa,ivperp,iz,ir] end @@ -97,11 +97,13 @@ function source_terms_evolve_ppar_no_collisions!(pdf_out, pdf_in, dens, upar, pp end if ion_source_settings.active - source_amplitude = moments.charged.external_source_amplitude - source_T = ion_source_settings.source_T + source_density_amplitude = moments.ion.external_source_density_amplitude + source_momentum_amplitude = moments.ion.external_source_momentum_amplitude + source_pressure_amplitude = moments.ion.external_source_pressure_amplitude @loop_r_z ir iz begin - term = dt * source_amplitude[iz,ir] * - (1.5/dens[iz,ir] - (0.25 * source_T + 0.5 * upar[iz,ir]^2) / ppar[iz,ir]) + term = dt * (1.5 * source_density_amplitude[iz,ir] / dens[iz,ir] - + (0.5 * source_pressure_amplitude[iz,ir] + + source_momentum_amplitude[iz,ir]) / ppar[iz,ir]) @loop_vperp_vpa ivperp ivpa begin pdf_out[ivpa,ivperp,iz,ir] -= term * pdf_in[ivpa,ivperp,iz,ir] end @@ -191,9 +193,9 @@ function source_terms_evolve_density_neutral!(pdf_out, pdf_in, dens, upar, ddens end if neutral_source_settings.active - source_amplitude = moments.neutral.external_source_amplitude + source_density_amplitude = moments.neutral.external_source_density_amplitude @loop_r_z ir iz begin - term = dt * source_amplitude[iz,ir] / dens[iz,ir] + term = dt * source_density_amplitude[iz,ir] / dens[iz,ir] @loop_vzeta_vr_vz ivzeta ivr ivz begin pdf_out[ivz,ivr,ivzeta,iz,ir] -= term * pdf_in[ivz,ivr,ivzeta,iz,ir] end @@ -222,11 +224,13 @@ function source_terms_evolve_ppar_no_collisions_neutral!(pdf_out, pdf_in, dens, end if neutral_source_settings.active - source_amplitude = moments.neutral.external_source_amplitude - source_T = neutral_source_settings.source_T + source_density_amplitude = moments.neutral.external_source_density_amplitude + source_momentum_amplitude = moments.neutral.external_source_momentum_amplitude + source_pressure_amplitude = moments.neutral.external_source_pressure_amplitude @loop_r_z ir iz begin - term = dt * source_amplitude[iz,ir] * - (1.5/dens[iz,ir] - (0.25 * source_T + 0.5 * upar[iz,ir]^2) / ppar[iz,ir]) + term = dt * (1.5 * source_density_amplitude[iz,ir] / dens[iz,ir] - + (0.5 * source_pressure_amplitude[iz,ir] + + source_momentum_amplitude[iz,ir]) / ppar[iz,ir]) @loop_vzeta_vr_vz ivzeta ivr ivz begin pdf_out[ivz,ivr,ivzeta,iz,ir] -= term * pdf_in[ivz,ivr,ivzeta,iz,ir] end @@ -257,7 +261,7 @@ end """ advance the dfn with an arbitrary source function """ -function source_terms_manufactured!(pdf_charged_out, pdf_neutral_out, vz, vr, vzeta, vpa, vperp, z, r, t, dt, composition, manufactured_source_list) +function source_terms_manufactured!(pdf_ion_out, pdf_neutral_out, vz, vr, vzeta, vpa, vperp, z, r, t, dt, composition, manufactured_source_list) if manufactured_source_list.time_independent_sources # the (time-independent) manufactured source arrays Source_i = manufactured_source_list.Source_i_array @@ -267,7 +271,7 @@ function source_terms_manufactured!(pdf_charged_out, pdf_neutral_out, vz, vr, vz @loop_s is begin @loop_r_z_vperp_vpa ir iz ivperp ivpa begin - pdf_charged_out[ivpa,ivperp,iz,ir,is] += dt*Source_i[ivpa,ivperp,iz,ir] + pdf_ion_out[ivpa,ivperp,iz,ir,is] += dt*Source_i[ivpa,ivperp,iz,ir] end end @@ -288,7 +292,7 @@ function source_terms_manufactured!(pdf_charged_out, pdf_neutral_out, vz, vr, vz @loop_s is begin @loop_r_z_vperp_vpa ir iz ivperp ivpa begin - pdf_charged_out[ivpa,ivperp,iz,ir,is] += dt*Source_i_func(vpa.grid[ivpa],vperp.grid[ivperp],z.grid[iz],r.grid[ir],t) + pdf_ion_out[ivpa,ivperp,iz,ir,is] += dt*Source_i_func(vpa.grid[ivpa],vperp.grid[ivperp],z.grid[iz],r.grid[ir],t) end end diff --git a/moment_kinetics/src/time_advance.jl b/moment_kinetics/src/time_advance.jl index f8506f656..e5cdc0ef4 100644 --- a/moment_kinetics/src/time_advance.jl +++ b/moment_kinetics/src/time_advance.jl @@ -4,6 +4,7 @@ module time_advance export setup_time_advance! export time_advance! +export allocate_advection_structs export setup_dummy_and_buffer_arrays using MPI @@ -12,7 +13,7 @@ using ..array_allocation: allocate_float, allocate_shared_float, allocate_shared using ..communication using ..communication: _block_synchronize using ..debugging -using ..file_io: write_data_to_ascii, write_moments_data_to_binary, write_dfns_data_to_binary, debug_dump +using ..file_io: write_data_to_ascii, write_all_moments_data_to_binary, write_all_dfns_data_to_binary, debug_dump using ..looping using ..moment_kinetics_structs: scratch_pdf using ..velocity_moments: update_moments!, update_moments_neutral!, reset_moments_status! @@ -20,12 +21,12 @@ using ..velocity_moments: update_density!, update_upar!, update_ppar!, update_pp using ..velocity_moments: update_neutral_density!, update_neutral_qz! using ..velocity_moments: update_neutral_uzeta!, update_neutral_uz!, update_neutral_ur! using ..velocity_moments: update_neutral_pzeta!, update_neutral_pz!, update_neutral_pr! -using ..velocity_moments: calculate_moment_derivatives!, calculate_moment_derivatives_neutral! +using ..velocity_moments: calculate_ion_moment_derivatives!, calculate_neutral_moment_derivatives! using ..velocity_moments: update_chodura! using ..velocity_grid_transforms: vzvrvzeta_to_vpavperp!, vpavperp_to_vzvrvzeta! -using ..initial_conditions: enforce_boundary_conditions! -using ..initial_conditions: enforce_neutral_boundary_conditions! -using ..input_structs: advance_info, time_info +using ..boundary_conditions: enforce_boundary_conditions! +using ..boundary_conditions: enforce_neutral_boundary_conditions! +using ..input_structs using ..moment_constraints: hard_force_moment_constraints!, hard_force_moment_constraints_neutral! using ..advection: setup_advection @@ -178,24 +179,71 @@ struct spectral_object_struct{Tvz,Tvr,Tvzeta,Tvpa,Tvperp,Tz,Tr} r_spectral::Tr end -""" -create arrays and do other work needed to setup -the main time advance loop. -this includes creating and populating structs -for Chebyshev transforms, velocity space moments, -EM fields, and advection terms -""" -function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz_spectral, - vr_spectral, vzeta_spectral, vpa_spectral, vperp_spectral, - z_spectral, r_spectral, composition, drive_input, moments, - t_input, code_time, dt_reload, dt_before_last_fail_reload, - collisions, species, geometry, boundary_distributions, - external_source_settings, num_diss_params, - manufactured_solns_input, restarting) +function allocate_advection_structs(composition, z, r, vpa, vperp, vz, vr, vzeta) # define some local variables for convenience/tidiness - n_species = composition.n_species n_ion_species = composition.n_ion_species n_neutral_species = composition.n_neutral_species + n_neutral_species_alloc = max(1,composition.n_neutral_species) + ## ## + # ion particle advection structs # + ## ## + # create structure z_advect whose members are the arrays needed to compute + # the advection term(s) appearing in the part of the ion kinetic equation dealing + # with advection in z + begin_serial_region() + z_advect = setup_advection(n_ion_species, z, vpa, vperp, r) + # create structure r_advect whose members are the arrays needed to compute + # the advection term(s) appearing in the split part of the ion kinetic equation dealing + # with advection in r + begin_serial_region() + r_advect = setup_advection(n_ion_species, r, vpa, vperp, z) + # create structure vpa_advect whose members are the arrays needed to compute + # the advection term(s) appearing in the split part of the ion kinetic equation dealing + # with advection in vpa + begin_serial_region() + vpa_advect = setup_advection(n_ion_species, vpa, vperp, z, r) + # create structure vperp_advect whose members are the arrays needed to compute + # the advection term(s) appearing in the split part of the ion kinetic equation dealing + # with advection in vperp + begin_serial_region() + vperp_advect = setup_advection(n_ion_species, vperp, vpa, z, r) + ## ## + # neutral particle advection structs # + ## ## + # create structure neutral_z_advect for neutral particle advection + begin_serial_region() + neutral_z_advect = setup_advection(n_neutral_species_alloc, z, vz, vr, vzeta, r) + # create structure neutral_r_advect for neutral particle advection + begin_serial_region() + neutral_r_advect = setup_advection(n_neutral_species_alloc, r, vz, vr, vzeta, z) + # create structure neutral_vz_advect for neutral particle advection + begin_serial_region() + neutral_vz_advect = setup_advection(n_neutral_species_alloc, vz, vr, vzeta, z, r) + ## ## + # construct named list of advection structs to compactify arguments # + ## ## + advection_structs = advect_object_struct(vpa_advect, vperp_advect, z_advect, r_advect, + neutral_z_advect, neutral_r_advect, neutral_vz_advect) + return advection_structs +end + +""" + setup_time_info(t_input; electrons=nothing) + +Create a [`input_structs.time_info`](@ref) struct using the settings in `t_input`. +""" +function setup_time_info(t_input, code_time, dt_reload, dt_before_last_fail_reload, + manufactured_solns_input, io_input) + rk_coefs, n_rk_stages, rk_order, adaptive, low_storage, CFL_prefactor = + setup_runge_kutta_coefficients!(t_input.type, + t_input.CFL_prefactor, + t_input.split_operators) + + if !adaptive + # No adaptive timestep, want to use the value from the input file even when we are + # restarting + dt_reload = nothing + end dt_shared = allocate_shared_float(1) previous_dt_shared = allocate_shared_float(1) @@ -215,37 +263,67 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz end_time = code_time + t_input.dt * t_input.nstep epsilon = 1.e-11 - moments_output_times = [code_time + i*t_input.dt - for i ∈ t_input.nwrite:t_input.nwrite:t_input.nstep] + if t_input.nwrite == 0 + moments_output_times = [end_time] + else + moments_output_times = [code_time + i*t_input.dt + for i ∈ t_input.nwrite:t_input.nwrite:t_input.nstep] + end if moments_output_times[end] < end_time - epsilon push!(moments_output_times, end_time) end - dfns_output_times = [code_time + i*t_input.dt - for i ∈ t_input.nwrite_dfns:t_input.nwrite_dfns:t_input.nstep] + if t_input.nwrite_dfns == 0 + dfns_output_times = [end_time] + else + dfns_output_times = [code_time + i*t_input.dt + for i ∈ t_input.nwrite_dfns:t_input.nwrite_dfns:t_input.nstep] + end if dfns_output_times[end] < end_time - epsilon push!(dfns_output_times, end_time) end - rk_coefs, n_rk_stages, rk_order, adaptive, low_storage, CFL_prefactor = - setup_runge_kutta_coefficients!(t_input.type, - t_input.CFL_prefactor, - t_input.split_operators) + if t_input.high_precision_error_sum error_sum_zero = Float128(0.0) else error_sum_zero = 0.0 end - t_params = time_info(t_input.nstep, end_time, dt_shared, previous_dt_shared, next_output_time, - dt_before_output, dt_before_last_fail, CFL_prefactor, - step_to_output, Ref(0), Ref(0), mk_int[], mk_int[], - moments_output_times, dfns_output_times, t_input.type, rk_coefs, - n_rk_stages, rk_order, adaptive, low_storage, t_input.rtol, - t_input.atol, t_input.atol_upar, t_input.step_update_prefactor, - t_input.max_increase_factor, - t_input.max_increase_factor_near_last_fail, - t_input.last_fail_proximity_factor, t_input.minimum_dt, - t_input.maximum_dt, error_sum_zero, t_input.split_operators, - t_input.steady_state_residual, t_input.converged_residual_value, - manufactured_solns_input.use_for_advance, t_input.stopfile_name) + return time_info(t_input.nstep, end_time, dt_shared, previous_dt_shared, next_output_time, + dt_before_output, dt_before_last_fail, CFL_prefactor, step_to_output, + Ref(0), Ref(0), mk_int[], mk_int[], moments_output_times, + dfns_output_times, t_input.type, rk_coefs, n_rk_stages, rk_order, + adaptive, low_storage, t_input.rtol, t_input.atol, t_input.atol_upar, + t_input.step_update_prefactor, t_input.max_increase_factor, + t_input.max_increase_factor_near_last_fail, + t_input.last_fail_proximity_factor, t_input.minimum_dt, + t_input.maximum_dt, error_sum_zero, t_input.split_operators, + t_input.steady_state_residual, t_input.converged_residual_value, + manufactured_solns_input.use_for_advance, t_input.stopfile_name) +end + +""" +create arrays and do other work needed to setup +the main time advance loop. +this includes creating and populating structs +for Chebyshev transforms, velocity space moments, +EM fields, and advection terms +""" +function setup_time_advance!(pdf, fields, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, + vz_spectral, vr_spectral, vzeta_spectral, vpa_spectral, + vperp_spectral, z_spectral, r_spectral, composition, + moments, t_input, code_time, dt_reload, + dt_before_last_fail_reload, collisions, species, geometry, + boundary_distributions, external_source_settings, + num_diss_params, manufactured_solns_input, advection_structs, + scratch_dummy, restarting) + # define some local variables for convenience/tidiness + n_ion_species = composition.n_ion_species + n_neutral_species = composition.n_neutral_species + ion_mom_diss_coeff = num_diss_params.ion.moment_dissipation_coefficient + electron_mom_diss_coeff = num_diss_params.electron.moment_dissipation_coefficient + neutral_mom_diss_coeff = num_diss_params.neutral.moment_dissipation_coefficient + + t_params = setup_time_info(t_input, code_time, dt_reload, dt_before_last_fail_reload, + manufactured_solns_input, io_input) # Make Vectors that count which variable caused timestep limits and timestep failures # the right length. Do this setup even when not using adaptive timestepping, because @@ -302,11 +380,9 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz # create an array of structs containing scratch arrays for the pdf and low-order moments # that may be evolved separately via fluid equations - scratch = setup_scratch_arrays(moments, pdf.charged.norm, pdf.neutral.norm, t_params.n_rk_stages) + scratch = setup_scratch_arrays(moments, pdf.ion.norm, pdf.neutral.norm, t_params.n_rk_stages) # setup dummy arrays & buffer arrays for z r MPI n_neutral_species_alloc = max(1,composition.n_neutral_species) - scratch_dummy = setup_dummy_and_buffer_arrays(r.n,z.n,vpa.n,vperp.n,vz.n,vr.n,vzeta.n, - composition.n_ion_species,n_neutral_species_alloc) # create arrays for Fokker-Planck collisions if advance.explicit_weakform_fp_collisions fp_arrays = init_fokker_planck_collisions_weak_form(vpa,vperp,vpa_spectral,vperp_spectral; precompute_weights=true) @@ -316,10 +392,6 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz # create gyroaverage matrix arrays gyroavs = init_gyro_operators(vperp,z,r,gyrophase,geometry,composition) - # create the "fields" structure that contains arrays - # for the electrostatic potential phi and eventually the electromagnetic fields - fields = setup_em_fields(vperp.n, z.n, r.n, composition.n_ion_species, drive_input.force_phi, - drive_input.amplitude, drive_input.frequency, drive_input.force_Er_zero_at_wall) # initialize the electrostatic potential begin_serial_region() update_phi!(fields, scratch[1], vperp, z, r, composition, z_spectral, r_spectral, scratch_dummy, gyroavs) @@ -332,62 +404,50 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz # 'speed' in advect objects, which are needed for boundary conditions on the # distribution function which is then used to (possibly) re-calculate the moments # after which the initial values of moment derivatives are re-calculated. - calculate_moment_derivatives!(moments, scratch[1], scratch_dummy, z, z_spectral, num_diss_params) - calculate_moment_derivatives_neutral!(moments, scratch[1], scratch_dummy, z, - z_spectral, num_diss_params) - + calculate_ion_moment_derivatives!(moments, scratch[1], scratch_dummy, z, z_spectral, + ion_mom_diss_coeff) + calculate_neutral_moment_derivatives!(moments, scratch[1], scratch_dummy, z, z_spectral, + neutral_mom_diss_coeff) + + r_advect = advection_structs.r_advect + z_advect = advection_structs.z_advect + vperp_advect = advection_structs.vperp_advect + vpa_advect = advection_structs.vpa_advect + neutral_r_advect = advection_structs.neutral_r_advect + neutral_z_advect = advection_structs.neutral_z_advect + neutral_vz_advect = advection_structs.neutral_vz_advect ## - # Charged particle advection only + # ion particle advection only ## - # create structure r_advect whose members are the arrays needed to compute - # the advection term(s) appearing in the split part of the GK equation dealing - # with advection in r - begin_serial_region() - r_advect = setup_advection(n_ion_species, r, vpa, vperp, z) if r.n > 1 # initialise the r advection speed begin_s_z_vperp_vpa_region() @loop_s is begin - @views update_speed_r!(r_advect[is], moments.charged.upar[:,:,is], - moments.charged.vth[:,:,is], fields, moments.evolve_upar, + @views update_speed_r!(r_advect[is], moments.ion.upar[:,:,is], + moments.ion.vth[:,:,is], fields, moments.evolve_upar, moments.evolve_ppar, vpa, vperp, z, r, geometry, is) end # enforce prescribed boundary condition in r on the distribution function f end - # create structure z_advect whose members are the arrays needed to compute - # the advection term(s) appearing in the split part of the GK equation dealing - # with advection in z - begin_serial_region() - z_advect = setup_advection(n_ion_species, z, vpa, vperp, r) if z.n > 1 # initialise the z advection speed begin_s_r_vperp_vpa_region() @loop_s is begin - @views update_speed_z!(z_advect[is], moments.charged.upar[:,:,is], - moments.charged.vth[:,:,is], moments.evolve_upar, + @views update_speed_z!(z_advect[is], moments.ion.upar[:,:,is], + moments.ion.vth[:,:,is], moments.evolve_upar, moments.evolve_ppar, fields, vpa, vperp, z, r, 0.0, geometry, is) end end - begin_serial_region() - # create structure vpa_advect whose members are the arrays needed to compute - # the advection term(s) appearing in the split part of the GK equation dealing - # with advection in vpa - vpa_advect = setup_advection(n_ion_species, vpa, vperp, z, r) # initialise the vpa advection speed begin_s_r_z_vperp_region() update_speed_vpa!(vpa_advect, fields, scratch[1], moments, vpa, vperp, z, r, composition, collisions, external_source_settings.ion, 0.0, geometry) - # create structure vperp_advect whose members are the arrays needed to compute - # the advection term(s) appearing in the split part of the GK equation dealing - # with advection in vperp - begin_serial_region() - vperp_advect = setup_advection(n_ion_species, vperp, vpa, z, r) # initialise the vperp advection speed # Note that z_advect and r_advect are arguments of update_speed_vperp! # This means that z_advect[is].speed and r_advect[is].speed are used to determine @@ -406,9 +466,6 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz # Neutral particle advection ## - # create structure neutral_r_advect for neutral particle advection - begin_serial_region() - neutral_r_advect = setup_advection(n_neutral_species_alloc, r, vz, vr, vzeta, z) if n_neutral_species > 0 && r.n > 1 # initialise the r advection speed begin_sn_z_vzeta_vr_vz_region() @@ -417,9 +474,6 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz end end - # create structure neutral_z_advect for neutral particle advection - begin_serial_region() - neutral_z_advect = setup_advection(n_neutral_species_alloc, z, vz, vr, vzeta, r) if n_neutral_species > 0 && z.n > 1 # initialise the z advection speed begin_sn_r_vzeta_vr_vz_region() @@ -432,9 +486,6 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz end end - # create structure neutral_vz_advect for neutral particle advection - begin_serial_region() - neutral_vz_advect = setup_advection(n_neutral_species_alloc, vz, vr, vzeta, z, r) if n_neutral_species > 0 # initialise the z advection speed @serial_region begin @@ -455,7 +506,6 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz # construct advect & spectral objects to compactify arguments ## - advect_objects = advect_object_struct(vpa_advect, vperp_advect, z_advect, r_advect, neutral_z_advect, neutral_r_advect, neutral_vz_advect) spectral_objects = spectral_object_struct(vz_spectral, vr_spectral, vzeta_spectral, vpa_spectral, vperp_spectral, z_spectral, r_spectral) if(advance.manufactured_solns_test) manufactured_source_list = manufactured_sources(manufactured_solns_input, r, z, @@ -469,43 +519,55 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz if !restarting begin_serial_region() # ensure initial pdf has no negative values - force_minimum_pdf_value!(pdf.charged.norm, num_diss_params) - force_minimum_pdf_value_neutral!(pdf.neutral.norm, num_diss_params) + force_minimum_pdf_value!(pdf.ion.norm, num_diss_params.ion.force_minimum_pdf_value) + force_minimum_pdf_value_neutral!(pdf.neutral.norm, num_diss_params.neutral.force_minimum_pdf_value) # enforce boundary conditions and moment constraints to ensure a consistent initial # condition enforce_boundary_conditions!( - pdf.charged.norm, boundary_distributions.pdf_rboundary_charged, - moments.charged.dens, moments.charged.upar, moments.charged.ppar, moments, + pdf.ion.norm, boundary_distributions.pdf_rboundary_ion, + moments.ion.dens, moments.ion.upar, moments.ion.ppar, moments, vpa.bc, z.bc, r.bc, vpa, vperp, z, r, vpa_spectral, vperp_spectral, vpa_advect, vperp_advect, z_advect, r_advect, composition, scratch_dummy, advance.r_diffusion, advance.vpa_diffusion, advance.vperp_diffusion) # Ensure normalised pdf exactly obeys integral constraints if evolving moments begin_s_r_z_region() - @loop_s_r_z is ir iz begin - @views hard_force_moment_constraints!(pdf.charged.norm[:,:,iz,ir,is], moments, - vpa) + if moments.evolve_density && moments.enforce_conservation + A = moments.ion.constraints_A_coefficient + B = moments.ion.constraints_B_coefficient + C = moments.ion.constraints_C_coefficient + @loop_s_r_z is ir iz begin + (A[iz,ir,is], B[iz,ir,is], C[iz,ir,is]) = + @views hard_force_moment_constraints!(pdf.ion.norm[:,:,iz,ir,is], + moments, vpa) + end end # update moments in case they were affected by applying boundary conditions or # constraints to the pdf reset_moments_status!(moments) - update_moments!(moments, pdf.charged.norm, gyroavs, vpa, vperp, z, r, composition, + update_moments!(moments, pdf.ion.norm, gyroavs, vpa, vperp, z, r, composition, r_spectral,geometry,scratch_dummy,z_advect) # enforce boundary conditions in r and z on the neutral particle distribution function if n_neutral_species > 0 # Note, so far vr and vzeta do not need advect objects, so pass `nothing` for # those as a placeholder enforce_neutral_boundary_conditions!( - pdf.neutral.norm, pdf.charged.norm, boundary_distributions, + pdf.neutral.norm, pdf.ion.norm, boundary_distributions, moments.neutral.dens, moments.neutral.uz, moments.neutral.pz, moments, - moments.charged.dens, moments.charged.upar, fields.Er, vzeta_spectral, + moments.ion.dens, moments.ion.upar, fields.Er, vzeta_spectral, vr_spectral, vz_spectral, neutral_r_advect, neutral_z_advect, nothing, nothing, neutral_vz_advect, r, z, vzeta, vr, vz, composition, geometry, scratch_dummy, advance.r_diffusion, advance.vz_diffusion) begin_sn_r_z_region() - @loop_sn_r_z isn ir iz begin - @views hard_force_moment_constraints_neutral!( - pdf.neutral.norm[:,:,:,iz,ir,isn], moments, vz) + if moments.evolve_density && moments.enforce_conservation + A = moments.neutral.constraints_A_coefficient + B = moments.neutral.constraints_B_coefficient + C = moments.neutral.constraints_C_coefficient + @loop_sn_r_z isn ir iz begin + (A[iz,ir,isn], B[iz,ir,isn], C[iz,ir,isn]) = + @views hard_force_moment_constraints_neutral!( + pdf.neutral.norm[:,:,:,iz,ir,isn], moments, vz) + end end update_moments_neutral!(moments, pdf.neutral.norm, vz, vr, vzeta, z, r, composition) @@ -515,10 +577,10 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz # or constraints to the pdf begin_s_r_z_region() @loop_s_r_z is ir iz begin - scratch[1].pdf[:,:,iz,ir,is] .= pdf.charged.norm[:,:,iz,ir,is] - scratch[1].density[iz,ir,is] = moments.charged.dens[iz,ir,is] - scratch[1].upar[iz,ir,is] = moments.charged.upar[iz,ir,is] - scratch[1].ppar[iz,ir,is] = moments.charged.ppar[iz,ir,is] + scratch[1].pdf[:,:,iz,ir,is] .= pdf.ion.norm[:,:,iz,ir,is] + scratch[1].density[iz,ir,is] = moments.ion.dens[iz,ir,is] + scratch[1].upar[iz,ir,is] = moments.ion.upar[iz,ir,is] + scratch[1].ppar[iz,ir,is] = moments.ion.ppar[iz,ir,is] end begin_sn_r_z_region(no_synchronize=true) @@ -532,16 +594,16 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz update_phi!(fields, scratch[1], vperp, z, r, composition, z_spectral, r_spectral, scratch_dummy, gyroavs) - calculate_moment_derivatives!(moments, scratch[1], scratch_dummy, z, z_spectral, num_diss_params) - calculate_moment_derivatives_neutral!(moments, scratch[1], scratch_dummy, z, - z_spectral, num_diss_params) + calculate_ion_moment_derivatives!(moments, scratch[1], scratch_dummy, z, z_spectral, + ion_mom_diss_coeff) + calculate_neutral_moment_derivatives!(moments, scratch[1], scratch_dummy, z, z_spectral, + neutral_mom_diss_coeff) # Ensure all processes are synchronized at the end of the setup _block_synchronize() - return moments, fields, spectral_objects, advect_objects, - scratch, advance, t_params, fp_arrays, gyroavs, scratch_dummy, - manufactured_source_list + return moments, spectral_objects, scratch, advance, t_params, fp_arrays, gyroavs, + manufactured_source_list end """ @@ -563,7 +625,7 @@ function setup_advance_flags(moments, composition, t_params, collisions, advance_ionization = false advance_ionization_1V = false advance_ionization_source = false - advance_krook_collisions = false + advance_krook_collisions_ii = false advance_external_source = false advance_numerical_dissipation = false advance_sources = false @@ -641,8 +703,8 @@ function setup_advance_flags(moments, composition, t_params, collisions, if collisions.ionization > 0.0 && collisions.constant_ionization_rate advance_ionization_source = true end - if collisions.krook.krook_collision_frequency_prefactor > 0.0 - advance_krook_collisions = true + if collisions.krook.nuii0 > 0.0 + advance_krook_collisions_ii = true end advance_external_source = external_source_settings.ion.active advance_neutral_external_source = external_source_settings.neutral.active @@ -682,11 +744,11 @@ function setup_advance_flags(moments, composition, t_params, collisions, end # flag to determine if a d^2/dr^2 operator is present - r_diffusion = (advance_numerical_dissipation && num_diss_params.r_dissipation_coefficient > 0.0) + r_diffusion = (advance_numerical_dissipation && num_diss_params.ion.r_dissipation_coefficient > 0.0) # flag to determine if a d^2/dvpa^2 operator is present - vpa_diffusion = ((advance_numerical_dissipation && num_diss_params.vpa_dissipation_coefficient > 0.0) || explicit_weakform_fp_collisions) - vperp_diffusion = ((advance_numerical_dissipation && num_diss_params.vperp_dissipation_coefficient > 0.0) || explicit_weakform_fp_collisions) - vz_diffusion = (advance_numerical_dissipation && num_diss_params.vz_dissipation_coefficient > 0.0) + vpa_diffusion = ((advance_numerical_dissipation && num_diss_params.ion.vpa_dissipation_coefficient > 0.0) || explicit_weakform_fp_collisions) + vperp_diffusion = ((advance_numerical_dissipation && num_diss_params.ion.vperp_dissipation_coefficient > 0.0) || explicit_weakform_fp_collisions) + vz_diffusion = (advance_numerical_dissipation && num_diss_params.neutral.vz_dissipation_coefficient > 0.0) end manufactured_solns_test = manufactured_solns_input.use_for_advance @@ -695,7 +757,7 @@ function setup_advance_flags(moments, composition, t_params, collisions, advance_neutral_z_advection, advance_neutral_r_advection, advance_neutral_vz_advection, advance_cx, advance_cx_1V, advance_ionization, advance_ionization_1V, - advance_ionization_source, advance_krook_collisions, + advance_ionization_source, advance_krook_collisions_ii, explicit_weakform_fp_collisions, advance_external_source, advance_numerical_dissipation, advance_sources, advance_continuity, advance_force_balance, @@ -833,12 +895,12 @@ end create an array of structs containing scratch arrays for the normalised pdf and low-order moments that may be evolved separately via fluid equations """ -function setup_scratch_arrays(moments, pdf_charged_in, pdf_neutral_in, n_rk_stages) +function setup_scratch_arrays(moments, pdf_ion_in, pdf_neutral_in, n_rk_stages) # create n_rk_stages+1 structs, each of which will contain one pdf, # one density, and one parallel flow array scratch = Vector{scratch_pdf{5,3,6,3}}(undef, n_rk_stages+1) - pdf_dims = size(pdf_charged_in) - moment_dims = size(moments.charged.dens) + pdf_dims = size(pdf_ion_in) + moment_dims = size(moments.ion.dens) pdf_neutral_dims = size(pdf_neutral_in) moment_neutral_dims = size(moments.neutral.dens) # populate each of the structs @@ -863,11 +925,11 @@ function setup_scratch_arrays(moments, pdf_charged_in, pdf_neutral_in, n_rk_stag pdf_neutral_array, density_neutral_array, uz_neutral_array, pz_neutral_array) @serial_region begin - scratch[istage].pdf .= pdf_charged_in - scratch[istage].density .= moments.charged.dens - scratch[istage].upar .= moments.charged.upar - scratch[istage].ppar .= moments.charged.ppar - scratch[istage].pperp .= moments.charged.pperp + scratch[istage].pdf .= pdf_ion_in + scratch[istage].density .= moments.ion.dens + scratch[istage].upar .= moments.ion.upar + scratch[istage].ppar .= moments.ion.ppar + scratch[istage].pperp .= moments.ion.pperp scratch[istage].pdf_neutral .= pdf_neutral_in scratch[istage].density_neutral .= moments.neutral.dens @@ -935,6 +997,11 @@ function time_advance!(pdf, scratch, t, t_params, vz, vr, vzeta, vpa, vperp, gyr iwrite_dfns = 2 finish_now = false t_params.step_counter[] = 1 + if t ≥ t_params.end_time - epsilon + # User must have requested zero output steps, i.e. to just write out the initial + # profiles + return nothing + end while true diagnostic_checks = (t + t_params.dt[] ≥ t_params.moments_output_times[moments_output_counter] - epsilon @@ -1059,12 +1126,13 @@ function time_advance!(pdf, scratch, t, t_params, vz, vr, vzeta, vpa, vperp, gyr print(Dates.format(now(), dateformat"H:MM:SS")) end end - write_data_to_ascii(moments, fields, vpa, vperp, z, r, t, + write_data_to_ascii(pdf, moments, fields, vpa, vperp, z, r, t, composition.n_ion_species, composition.n_neutral_species, ascii_io) - write_moments_data_to_binary(moments, fields, t, composition.n_ion_species, - composition.n_neutral_species, io_moments, - iwrite_moments, time_for_run, t_params, r, z) + write_all_moments_data_to_binary(moments, fields, t, + composition.n_ion_species, + composition.n_neutral_species, io_moments, + iwrite_moments, time_for_run, t_params, r, z) if t_params.steady_state_residual # Calculate some residuals to see how close simulation is to steady state @@ -1144,11 +1212,11 @@ function time_advance!(pdf, scratch, t, t_params, vz, vr, vzeta, vpa, vperp, gyr flush(stdout) end end - write_dfns_data_to_binary(pdf.charged.norm, pdf.neutral.norm, moments, fields, - t, composition.n_ion_species, - composition.n_neutral_species, io_dfns, iwrite_dfns, - time_for_run, t_params, r, z, vperp, vpa, vzeta, vr, - vz) + write_all_dfns_data_to_binary(pdf, moments, fields, t, + composition.n_ion_species, + composition.n_neutral_species, io_dfns, + iwrite_dfns, time_for_run, t_params, r, z, + vperp, vpa, vzeta, vr, vz) iwrite_dfns += 1 begin_s_r_z_vperp_region() @debug_detect_redundant_block_synchronize begin @@ -1190,7 +1258,7 @@ function time_advance_split_operators!(pdf, scratch, t, t_params, vpa, z, flipflop = (mod(istep,2)==0) if flipflop # advance the operator-split 1D advection equation in vpa - # vpa-advection only applies for charged species + # vpa-advection only applies for ion species advance.vpa_advection = true time_advance_no_splitting!(pdf, scratch, t, t_params, vpa, z, vpa_spectral, z_spectral, moments, fields, vpa_advect, z_advect, @@ -1198,7 +1266,7 @@ function time_advance_split_operators!(pdf, scratch, t, t_params, vpa, z, istep) advance.vpa_advection = false # z_advection! advances the operator-split 1D advection equation in z - # apply z-advection operation to all species (charged and neutral) + # apply z-advection operation to all species (ion and neutral) advance.z_advection = true time_advance_no_splitting!(pdf, scratch, t, t_params, vpa, z, vpa_spectral, z_spectral, moments, fields, vpa_advect, z_advect, @@ -1225,12 +1293,12 @@ function time_advance_split_operators!(pdf, scratch, t, t_params, vpa, z, end end if collisions.krook_collision_frequency_prefactor > 0.0 - advance.krook_collisions = true + advance.krook_collisions_ii = true time_advance_no_splitting!(pdf, scratch, t, t_params, z, vpa, z_spectral, vpa_spectral, moments, fields, z_advect, vpa_advect, z_SL, vpa_SL, composition, collisions, sources, num_diss_params, advance, istep) - advance.krook_collisions = false + advance.krook_collisions_ii = false end # and add the source terms associated with redefining g = pdf/density or pdf*vth/density # to the kinetic equation @@ -1327,7 +1395,7 @@ function time_advance_split_operators!(pdf, scratch, t, t_params, vpa, z, end end # z_advection! advances the operator-split 1D advection equation in z - # apply z-advection operation to all species (charged and neutral) + # apply z-advection operation to all species (ion and neutral) advance.z_advection = true time_advance_no_splitting!(pdf, scratch, t, t_params, vpa, z, vpa_spectral, z_spectral, moments, fields, vpa_advect, z_advect, @@ -1335,7 +1403,7 @@ function time_advance_split_operators!(pdf, scratch, t, t_params, vpa, z, istep) advance.z_advection = false # advance the operator-split 1D advection equation in vpa - # vpa-advection only applies for charged species + # vpa-advection only applies for ion species advance.vpa_advection = true time_advance_no_splitting!(pdf, scratch, t, t_params, vpa, z, vpa_spectral, z_spectral, moments, fields, vpa_advect, z_advect, @@ -1386,7 +1454,7 @@ function rk_update!(scratch, pdf, moments, fields, boundary_distributions, vz, v neutral_z_advect, neutral_r_advect, neutral_vz_advect = advect_objects.neutral_z_advect, advect_objects.neutral_r_advect, advect_objects.neutral_vz_advect ## - # update the charged particle distribution and moments + # update the ion distribution and moments ## # here we seem to have duplicate arrays for storing n, u||, p||, etc, but not for vth # 'scratch' is for the multiple stages of time advanced quantities, but 'moments' can be updated directly at each stage @@ -1397,7 +1465,7 @@ function rk_update!(scratch, pdf, moments, fields, boundary_distributions, vz, v # Ensure there are no negative values in the pdf before applying boundary # conditions, so that negative deviations do not mess up the integral-constraint # corrections in the sheath boundary conditions. - force_minimum_pdf_value!(new_scratch.pdf, num_diss_params) + force_minimum_pdf_value!(new_scratch.pdf, num_diss_params.ion.force_minimum_pdf_value) # Enforce boundary conditions in z and vpa on the distribution function. # Must be done after Runge Kutta update so that the boundary condition applied to @@ -1407,16 +1475,20 @@ function rk_update!(scratch, pdf, moments, fields, boundary_distributions, vz, v # contribution from one or more of the terms. # NB: probably need to do the same for the evolved moments enforce_boundary_conditions!(new_scratch, moments, - boundary_distributions.pdf_rboundary_charged, vpa.bc, z.bc, r.bc, vpa, vperp, z, + boundary_distributions.pdf_rboundary_ion, vpa.bc, z.bc, r.bc, vpa, vperp, z, r, vpa_spectral, vperp_spectral, vpa_advect, vperp_advect, z_advect, r_advect, composition, scratch_dummy, advance.r_diffusion, advance.vpa_diffusion, advance.vperp_diffusion) if moments.evolve_density && moments.enforce_conservation begin_s_r_z_region() + A = moments.ion.constraints_A_coefficient + B = moments.ion.constraints_B_coefficient + C = moments.ion.constraints_C_coefficient @loop_s_r_z is ir iz begin - @views hard_force_moment_constraints!(new_scratch.pdf[:,:,iz,ir,is], moments, - vpa) + (A[iz,ir,is], B[iz,ir,is], C[iz,ir,is]) = + @views hard_force_moment_constraints!(new_scratch.pdf[:,:,iz,ir,is], + moments, vpa) end end @@ -1428,8 +1500,8 @@ function rk_update!(scratch, pdf, moments, fields, boundary_distributions, vz, v update_derived_moments!(new_scratch, moments, vpa, vperp, z, r, composition, r_spectral, geometry, gyroavs, scratch_dummy, z_advect, diagnostic_moments) - calculate_moment_derivatives!(moments, new_scratch, scratch_dummy, z, z_spectral, - num_diss_params) + calculate_ion_moment_derivatives!(moments, new_scratch, scratch_dummy, z, z_spectral, + num_diss_params.ion.moment_dissipation_coefficient) end update_derived_ion_moments_and_derivatives() @@ -1444,7 +1516,7 @@ function rk_update!(scratch, pdf, moments, fields, boundary_distributions, vz, v # Ensure there are no negative values in the pdf before applying boundary # conditions, so that negative deviations do not mess up the integral-constraint # corrections in the sheath boundary conditions. - force_minimum_pdf_value_neutral!(new_scratch.pdf_neutral, num_diss_params) + force_minimum_pdf_value_neutral!(new_scratch.pdf_neutral, num_diss_params.neutral.force_minimum_pdf_value) # Enforce boundary conditions in z and vpa on the distribution function. # Must be done after Runge Kutta update so that the boundary condition applied to @@ -1465,9 +1537,13 @@ function rk_update!(scratch, pdf, moments, fields, boundary_distributions, vz, v if moments.evolve_density && moments.enforce_conservation begin_sn_r_z_region() + A = moments.neutral.constraints_A_coefficient + B = moments.neutral.constraints_B_coefficient + C = moments.neutral.constraints_C_coefficient @loop_sn_r_z isn ir iz begin - @views hard_force_moment_constraints_neutral!( - new_scratch.pdf_neutral[:,:,:,iz,ir,isn], moments, vz) + (A[iz,ir,isn], B[iz,ir,isn], C[iz,ir,isn]) = + @views hard_force_moment_constraints_neutral!( + new_scratch.pdf_neutral[:,:,:,iz,ir,isn], moments, vz) end end @@ -1488,8 +1564,9 @@ function rk_update!(scratch, pdf, moments, fields, boundary_distributions, vz, v r, composition, moments.evolve_density, moments.evolve_upar, moments.evolve_ppar) - calculate_moment_derivatives_neutral!(moments, new_scratch, scratch_dummy, z, - z_spectral, num_diss_params) + calculate_neutral_moment_derivatives!(moments, new_scratch, scratch_dummy, z, + z_spectral, + num_diss_params.neutral.moment_dissipation_coefficient) end update_derived_neutral_moments_and_derivatives() end @@ -1587,9 +1664,8 @@ function adaptive_timestep_update!(scratch, t, t_params, moments, fields, compos begin_r_vperp_vpa_region(; no_synchronize=true) ion_z_CFL = Inf @loop_s is begin - update_speed_z!(z_advect[is], moments.charged.upar, moments.charged.vth, - evolve_upar, evolve_ppar, fields, vpa, vperp, z, r, t, geometry, - is) + update_speed_z!(z_advect[is], moments.ion.upar, moments.ion.vth, evolve_upar, + evolve_ppar, fields, vpa, vperp, z, r, t, geometry, is) this_minimum = get_minimum_CFL_z(z_advect[is].speed, z) @serial_region begin ion_z_CFL = min(ion_z_CFL, this_minimum) @@ -1752,7 +1828,7 @@ function adaptive_timestep_update!(scratch, t, t_params, moments, fields, compos end """ -update velocity moments that are calculable from the evolved charged pdf +update velocity moments that are calculable from the evolved ion pdf """ function update_derived_moments!(new_scratch, moments, vpa, vperp, z, r, composition, r_spectral, geometry, gyroavs, scratch_dummy, z_advect, diagnostic_moments) @@ -1766,20 +1842,20 @@ function update_derived_moments!(new_scratch, moments, vpa, vperp, z, r, composi end if !moments.evolve_density - update_density!(new_scratch.density, moments.charged.dens_updated, + update_density!(new_scratch.density, moments.ion.dens_updated, ff, vpa, vperp, z, r, composition) end if !moments.evolve_upar - update_upar!(new_scratch.upar, moments.charged.upar_updated, new_scratch.density, + update_upar!(new_scratch.upar, moments.ion.upar_updated, new_scratch.density, new_scratch.ppar, ff, vpa, vperp, z, r, composition, moments.evolve_density, moments.evolve_ppar) end if !moments.evolve_ppar # update_ppar! 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) - update_ppar!(new_scratch.ppar, moments.charged.ppar_updated, new_scratch.density, + update_ppar!(new_scratch.ppar, moments.ion.ppar_updated, new_scratch.density, new_scratch.upar, ff, vpa, vperp, z, r, composition, moments.evolve_density, moments.evolve_upar) - end + end update_pperp!(new_scratch.pperp, ff, vpa, vperp, z, r, composition) # if diagnostic time step/RK stage @@ -1790,7 +1866,7 @@ function update_derived_moments!(new_scratch, moments, vpa, vperp, z, r, composi # update the thermal speed begin_s_r_z_region() try #below block causes DomainError if ppar < 0 or density, so exit cleanly if possible - update_vth!(moments.charged.vth, new_scratch.ppar, new_scratch.pperp, new_scratch.density, vperp, z, r, composition) + update_vth!(moments.ion.vth, new_scratch.ppar, new_scratch.pperp, new_scratch.density, vperp, z, r, composition) catch e if global_size[] > 1 println("ERROR: error calculating vth in time_advance.jl") @@ -1803,8 +1879,8 @@ function update_derived_moments!(new_scratch, moments, vpa, vperp, z, r, composi rethrow(e) end # update the parallel heat flux - update_qpar!(moments.charged.qpar, moments.charged.qpar_updated, new_scratch.density, - new_scratch.upar, moments.charged.vth, ff, vpa, vperp, z, r, + update_qpar!(moments.ion.qpar, moments.ion.qpar_updated, new_scratch.density, + new_scratch.upar, moments.ion.vth, ff, vpa, vperp, z, r, composition, moments.evolve_density, moments.evolve_upar, moments.evolve_ppar) # add further moments to be computed here @@ -1847,13 +1923,13 @@ function ssp_rk!(pdf, scratch, t, t_params, vz, vr, vzeta, vpa, vperp, gyrophase first_scratch = scratch[1] @loop_s_r_z_vperp_vpa is ir iz ivperp ivpa begin - first_scratch.pdf[ivpa,ivperp,iz,ir,is] = pdf.charged.norm[ivpa,ivperp,iz,ir,is] + first_scratch.pdf[ivpa,ivperp,iz,ir,is] = pdf.ion.norm[ivpa,ivperp,iz,ir,is] end @loop_s_r_z is ir iz begin - first_scratch.density[iz,ir,is] = moments.charged.dens[iz,ir,is] - first_scratch.upar[iz,ir,is] = moments.charged.upar[iz,ir,is] - first_scratch.ppar[iz,ir,is] = moments.charged.ppar[iz,ir,is] - first_scratch.pperp[iz,ir,is] = moments.charged.pperp[iz,ir,is] + first_scratch.density[iz,ir,is] = moments.ion.dens[iz,ir,is] + first_scratch.upar[iz,ir,is] = moments.ion.upar[iz,ir,is] + first_scratch.ppar[iz,ir,is] = moments.ion.ppar[iz,ir,is] + first_scratch.pperp[iz,ir,is] = moments.ion.pperp[iz,ir,is] end if composition.n_neutral_species > 0 @@ -1899,13 +1975,13 @@ function ssp_rk!(pdf, scratch, t, t_params, vz, vr, vzeta, vpa, vperp, gyrophase begin_s_r_z_region() final_scratch = scratch[istage] @loop_s_r_z_vperp_vpa is ir iz ivperp ivpa begin - pdf.charged.norm[ivpa,ivperp,iz,ir,is] = final_scratch.pdf[ivpa,ivperp,iz,ir,is] + pdf.ion.norm[ivpa,ivperp,iz,ir,is] = final_scratch.pdf[ivpa,ivperp,iz,ir,is] end @loop_s_r_z is ir iz begin - moments.charged.dens[iz,ir,is] = final_scratch.density[iz,ir,is] - moments.charged.upar[iz,ir,is] = final_scratch.upar[iz,ir,is] - moments.charged.ppar[iz,ir,is] = final_scratch.ppar[iz,ir,is] - moments.charged.pperp[iz,ir,is] = final_scratch.pperp[iz,ir,is] + moments.ion.dens[iz,ir,is] = final_scratch.density[iz,ir,is] + moments.ion.upar[iz,ir,is] = final_scratch.upar[iz,ir,is] + moments.ion.ppar[iz,ir,is] = final_scratch.ppar[iz,ir,is] + moments.ion.pperp[iz,ir,is] = final_scratch.pperp[iz,ir,is] end if composition.n_neutral_species > 0 # No need to synchronize here as we only change neutral quantities and previous @@ -1978,7 +2054,7 @@ function euler_time_advance!(fvec_out, fvec_in, pdf, fields, moments, n_ion_species = composition.n_ion_species n_neutral_species = composition.n_neutral_species # vpa_advection! advances the 1D advection equation in vpa. - # only charged species have a force accelerating them in vpa; + # only ion species have a force accelerating them in vpa; # however, neutral species do have non-zero d(wpa)/dt, so there is advection in wpa vpa_spectral, vperp_spectral, r_spectral, z_spectral = spectral_objects.vpa_spectral, spectral_objects.vperp_spectral, spectral_objects.r_spectral, spectral_objects.z_spectral @@ -2001,7 +2077,7 @@ function euler_time_advance!(fvec_out, fvec_in, pdf, fields, moments, end # z_advection! advances 1D advection equation in z - # apply z-advection operation to charged species + # apply z-advection operation to ion species if advance.z_advection z_advection!(fvec_out.pdf, fvec_in, moments, fields, z_advect, z, vpa, vperp, r, @@ -2051,9 +2127,9 @@ function euler_time_advance!(fvec_out, fvec_in, pdf, fields, moments, end if advance.cx_collisions || advance.ionization_collisions - # gyroaverage neutral dfn and place it in the charged.buffer array for use in the collisions step - vzvrvzeta_to_vpavperp!(pdf.charged.buffer, fvec_in.pdf_neutral, vz, vr, vzeta, vpa, vperp, gyrophase, z, r, geometry, composition) - # interpolate charged particle dfn and place it in the neutral.buffer array for use in the collisions step + # gyroaverage neutral dfn and place it in the ion.buffer array for use in the collisions step + vzvrvzeta_to_vpavperp!(pdf.ion.buffer, fvec_in.pdf_neutral, vz, vr, vzeta, vpa, vperp, gyrophase, z, r, geometry, composition) + # interpolate ion particle dfn and place it in the neutral.buffer array for use in the collisions step vpavperp_to_vzvrvzeta!(pdf.neutral.buffer, fvec_in.pdf, vz, vr, vzeta, vpa, vperp, z, r, geometry, composition) end @@ -2064,7 +2140,7 @@ function euler_time_advance!(fvec_out, fvec_in, pdf, fields, moments, collisions.charge_exchange, vpa_spectral, vz_spectral, dt) elseif advance.cx_collisions - charge_exchange_collisions_3V!(fvec_out.pdf, fvec_out.pdf_neutral, pdf.charged.buffer, pdf.neutral.buffer, fvec_in, composition, + charge_exchange_collisions_3V!(fvec_out.pdf, fvec_out.pdf_neutral, pdf.ion.buffer, pdf.neutral.buffer, fvec_in, composition, vz, vr, vzeta, vpa, vperp, z, r, collisions.charge_exchange, dt) end # account for ionization collisions between ions and neutrals @@ -2073,7 +2149,7 @@ function euler_time_advance!(fvec_out, fvec_in, pdf, fields, moments, vperp, z, r, vz_spectral, moments, composition, collisions, dt) elseif advance.ionization_collisions - ionization_collisions_3V!(fvec_out.pdf, fvec_out.pdf_neutral, pdf.charged.buffer, fvec_in, composition, + ionization_collisions_3V!(fvec_out.pdf, fvec_out.pdf_neutral, pdf.ion.buffer, fvec_in, composition, vz, vr, vzeta, vpa, vperp, z, r, collisions, dt) end if advance.ionization_source @@ -2082,7 +2158,7 @@ function euler_time_advance!(fvec_out, fvec_in, pdf, fields, moments, end # Add Krook collision operator for ions - if advance.krook_collisions + if advance.krook_collisions_ii krook_collisions!(fvec_out.pdf, fvec_in, moments, composition, collisions, vperp, vpa, dt) end @@ -2099,32 +2175,32 @@ function euler_time_advance!(fvec_out, fvec_in, pdf, fields, moments, # add numerical dissipation if advance.numerical_dissipation vpa_dissipation!(fvec_out.pdf, fvec_in.pdf, vpa, vpa_spectral, dt, - num_diss_params) + num_diss_params.ion.vpa_dissipation_coefficient) vperp_dissipation!(fvec_out.pdf, fvec_in.pdf, vperp, vperp_spectral, dt, - num_diss_params) + num_diss_params.ion.vperp_dissipation_coefficient) z_dissipation!(fvec_out.pdf, fvec_in.pdf, z, z_spectral, dt, - num_diss_params, scratch_dummy) + num_diss_params.ion.z_dissipation_coefficient, scratch_dummy) r_dissipation!(fvec_out.pdf, fvec_in.pdf, r, r_spectral, dt, - num_diss_params, scratch_dummy) + num_diss_params.ion.r_dissipation_coefficient, scratch_dummy) vz_dissipation_neutral!(fvec_out.pdf_neutral, fvec_in.pdf_neutral, vz, - vz_spectral, dt, num_diss_params) + vz_spectral, dt, num_diss_params.neutral.vz_dissipation_coefficient) z_dissipation_neutral!(fvec_out.pdf_neutral, fvec_in.pdf_neutral, z, z_spectral, - dt, num_diss_params, scratch_dummy) + dt, num_diss_params.neutral.z_dissipation_coefficient, scratch_dummy) r_dissipation_neutral!(fvec_out.pdf_neutral, fvec_in.pdf_neutral, r, r_spectral, - dt, num_diss_params, scratch_dummy) + dt, num_diss_params.neutral.r_dissipation_coefficient, scratch_dummy) end # advance with the Fokker-Planck self-collision operator if advance.explicit_weakform_fp_collisions update_entropy_diagnostic = (istage == 1) if collisions.fkpl.self_collisions # self collisions for each species - explicit_fokker_planck_collisions_weak_form!(fvec_out.pdf,fvec_in.pdf,moments.charged.dSdt,composition, + explicit_fokker_planck_collisions_weak_form!(fvec_out.pdf,fvec_in.pdf,moments.ion.dSdt,composition, collisions,dt,fp_arrays,r,z,vperp,vpa,vperp_spectral,vpa_spectral,scratch_dummy, diagnose_entropy_production = update_entropy_diagnostic) end if collisions.fkpl.slowing_down_test # include cross-collsions with fixed Maxwellian backgrounds - explicit_fp_collisions_weak_form_Maxwellian_cross_species!(fvec_out.pdf,fvec_in.pdf,moments.charged.dSdt, + explicit_fp_collisions_weak_form_Maxwellian_cross_species!(fvec_out.pdf,fvec_in.pdf,moments.ion.dSdt, composition,collisions,dt,fp_arrays,r,z,vperp,vpa,vperp_spectral,vpa_spectral, diagnose_entropy_production = update_entropy_diagnostic) end diff --git a/moment_kinetics/src/utils.jl b/moment_kinetics/src/utils.jl index 6ac8c2754..b2a434435 100644 --- a/moment_kinetics/src/utils.jl +++ b/moment_kinetics/src/utils.jl @@ -8,11 +8,13 @@ export get_unnormalized_parameters, print_unnormalized_parameters, to_seconds, t using ..communication using ..constants +using ..input_structs using ..looping using ..moment_kinetics_input: mk_input using ..reference_parameters using Dates +using Glob using MPI using OrderedCollections using TOML @@ -125,6 +127,173 @@ Convert a time period `x` to seconds """ to_hours(x::T) where {T<:TimePeriod} = x/convert(T, Hour(1)) +# Utility functions used for restarting + +""" +Append a number to the filename, to get a new, non-existing filename to backup the file +to. +""" +function get_backup_filename(filename) + if !isfile(filename) + error("Requested to restart from $filename, but this file does not exist") + end + counter = 1 + temp, extension = splitext(filename) + extension = extension[2:end] + temp, iblock_or_type = splitext(temp) + iblock_or_type = iblock_or_type[2:end] + iblock = nothing + basename = nothing + type = nothing + if iblock_or_type ∈ ("dfns", "initial_electron") + iblock = nothing + type = iblock_or_type + basename = temp + parallel_io = true + else + # Filename had an iblock, so we are not using parallel I/O, but actually want to + # use the iblock for this block, not necessarily for the exact file that was + # passed. + iblock = iblock_index[] + basename, type = splitext(temp) + type = type[2:end] + parallel_io = false + end + if type ∉ ("dfns", "initial_electron") + error("Must pass the '.dfns.h5' output file for restarting. Got $filename.") + end + backup_dfns_filename = "" + if parallel_io + # Using parallel I/O + while true + backup_dfns_filename = "$(basename)_$(counter).$(type).$(extension)" + if !isfile(backup_dfns_filename) + break + end + counter += 1 + end + # Create dfns_filename here even though it is the filename passed in, as + # parallel_io=false branch needs to get the right `iblock` for this block. + dfns_filename = "$(basename).$(type).$(extension)" + if type == "dfns" + moments_filename = "$(basename).moments.$(extension)" + else + moments_filename = nothing + end + backup_moments_filename = "$(basename)_$(counter).moments.$(extension)" + else + while true + backup_dfns_filename = "$(basename)_$(counter).$(type).$(iblock).$(extension)" + if !isfile(backup_dfns_filename) + break + end + counter += 1 + end + # Create dfns_filename here even though it is almost the filename passed in, in + # order to get the right `iblock` for this block. + dfns_filename = "$(basename).$(type).$(iblock).$(extension)" + if type == "dfns" + moments_filename = "$(basename).moments.$(iblock).$(extension)" + else + moments_filename = nothing + end + backup_moments_filename = "$(basename)_$(counter).moments.$(iblock).$(extension)" + end + backup_dfns_filename == "" && error("Failed to find a name for backup file.") + backup_prefix_iblock = ("$(basename)_$(counter)", iblock) + original_prefix_iblock = (basename, iblock) + return dfns_filename, backup_dfns_filename, parallel_io, moments_filename, + backup_moments_filename, backup_prefix_iblock, original_prefix_iblock +end + +""" + get_default_restart_filename(io_input, prefix; error_if_no_file_found=true) + +Get the default name for the file to restart from, using the input from `io_input`. + +`prefix` gives the type of file to open, e.g. "moments", "dfns", or "initial_electron". + +If no matching file is found, raise an error unless `error_if_no_file_found=false` is +passed, in which case no error is raised and instead the function returns `nothing`. +""" +function get_default_restart_filename(io_input, prefix; error_if_no_file_found=true) + binary_format = io_input.binary_format + if binary_format === hdf5 + ext = "h5" + elseif binary_format === netcdf + ext = "cdf" + else + error("Unrecognized binary_format '$binary_format'") + end + restart_filename_pattern = joinpath(io_input.output_dir, io_input.run_name * ".$prefix*." * ext) + restart_filename_glob = glob(restart_filename_pattern) + if length(restart_filename_glob) == 0 + if error_if_no_file_found + error("No '$prefix' output file to restart from found matching the pattern " + * "$restart_filename_pattern") + end + restart_filename = nothing + else + restart_filename = restart_filename_glob[1] + end + return restart_filename +end + +""" + get_prefix_iblock_and_move_existing_file(restart_filename, output_dir) + +Move `restart_filename` to a backup location (if it is in `output_dir`), returning a +prefix and block-index (which might be `nothing`) which can be used to open the file for +reloading variables. +""" +function get_prefix_iblock_and_move_existing_file(restart_filename, output_dir) + # Move the output file being restarted from to make sure it doesn't get + # overwritten. + dfns_filename, backup_dfns_filename, parallel_io, moments_filename, + backup_moments_filename, backup_prefix_iblock, original_prefix_iblock = + get_backup_filename(restart_filename) + + # Ensure every process got the filenames and checked files exist before moving + # files + MPI.Barrier(comm_world) + + if abspath(output_dir) == abspath(dirname(dfns_filename)) + # Only move the file if it is in our current run directory. Otherwise we are + # restarting from another run, and will not be overwriting the file. + if (parallel_io && global_rank[] == 0) || (!parallel_io && block_rank[] == 0) + mv(dfns_filename, backup_dfns_filename) + if moments_filename !== nothing + mv(moments_filename, backup_moments_filename) + end + end + else + # Reload from dfns_filename without moving the file + backup_prefix_iblock = original_prefix_iblock + end + + # Ensure files have been moved before any process tries to read from them + MPI.Barrier(comm_world) + + return backup_prefix_iblock +end + +""" + enum_from_string(enum_type, name) + +Get an the value of `enum_type`, whose name is given by the String (or Symbol) `name`. + +Returns `nothing` if the name is not found. +""" +function enum_from_string(enum_type, name) + name = Symbol(name) + for e ∈ instances(enum_type) + if name == Symbol(e) + return e + end + end + return nothing +end + # Utility functions for timestepping """ diff --git a/moment_kinetics/src/velocity_moments.jl b/moment_kinetics/src/velocity_moments.jl index 3da3e251f..a0c7388b3 100644 --- a/moment_kinetics/src/velocity_moments.jl +++ b/moment_kinetics/src/velocity_moments.jl @@ -5,7 +5,7 @@ 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 create_moments_ion, create_moments_neutral export update_moments! export update_density! export update_upar! @@ -14,7 +14,6 @@ export update_pperp! export update_qpar! export update_vth! export reset_moments_status! -export moments_chrg_substruct, moments_ntrl_substruct export update_neutral_density! export update_neutral_uz! export update_neutral_ur! @@ -42,6 +41,9 @@ using ..derivatives: derivative_z! using ..derivatives: derivative_r! using ..looping using ..gyroaverages: gyro_operators, gyroaverage_pdf! +using ..moment_kinetics_structs: moments_ion_substruct, + moments_neutral_substruct + #global tmpsum1 = 0.0 #global tmpsum2 = 0.0 #global dens_hist = zeros(17,1) @@ -49,172 +51,8 @@ using ..gyroaverages: gyro_operators, gyroaverage_pdf! """ """ -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 perpendicular pressure - pperp::MPISharedArray{mk_float,3} - # 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 z-derivative of the particle density - ddens_dz::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} - # this is the entropy production dS/dt = - int (ln f sum_s' C_ss' [f_s,f_s']) d^3 v - dSdt::MPISharedArray{mk_float,3} - # Spatially varying amplitude of the external source term - external_source_amplitude::MPISharedArray{mk_float,2} - # Spatially varying amplitude of the density moment of the external source term - external_source_density_amplitude::MPISharedArray{mk_float,2} - # Spatially varying amplitude of the parallel momentum moment of the external source - # term - external_source_momentum_amplitude::MPISharedArray{mk_float,2} - # Spatially varying amplitude of the parallel pressure moment of the external source - # term - external_source_pressure_amplitude::MPISharedArray{mk_float,2} - # Integral term for the PID controller of the external source term - external_source_controller_integral::MPISharedArray{mk_float,2} -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::Union{MPISharedArray{mk_float,3},Nothing} - # 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} - # Spatially varying amplitude of the external source term - external_source_amplitude::MPISharedArray{mk_float,2} - # Spatially varying amplitude of the density moment of the external source term - external_source_density_amplitude::MPISharedArray{mk_float,2} - # Spatially varying amplitude of the parallel momentum moment of the external source - # term - external_source_momentum_amplitude::MPISharedArray{mk_float,2} - # Spatially varying amplitude of the parallel pressure moment of the external source - # term - external_source_pressure_amplitude::MPISharedArray{mk_float,2} - # Integral term for the PID controller of the external source term - external_source_controller_integral::MPISharedArray{mk_float,2} -end - -""" -""" -function create_moments_charged(nz, nr, n_species, evolve_density, evolve_upar, - evolve_ppar, ion_source_settings, numerical_dissipation) +function create_moments_ion(nz, nr, n_species, evolve_density, evolve_upar, + evolve_ppar, ion_source_settings, num_diss_params) # 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 @@ -258,8 +96,7 @@ function create_moments_charged(nz, nr, n_species, evolve_density, evolve_upar, ddens_dz = nothing ddens_dz_upwind = nothing end - if evolve_density && - numerical_dissipation.moment_dissipation_coefficient > 0.0 + if evolve_density && num_diss_params.ion.moment_dissipation_coefficient > 0.0 d2dens_dz2 = allocate_shared_float(nz, nr, n_species) else @@ -275,8 +112,7 @@ function create_moments_charged(nz, nr, n_species, evolve_density, evolve_upar, else dupar_dz_upwind = nothing end - if evolve_upar && - numerical_dissipation.moment_dissipation_coefficient > 0.0 + if evolve_upar && num_diss_params.ion.moment_dissipation_coefficient > 0.0 d2upar_dz2 = allocate_shared_float(nz, nr, n_species) else @@ -336,8 +172,18 @@ function create_moments_charged(nz, nr, n_species, evolve_density, evolve_upar, external_source_controller_integral = allocate_shared_float(1, 1) end + if evolve_density || evolve_upar || evolve_ppar + constraints_A_coefficient = allocate_shared_float(nz, nr, n_species) + constraints_B_coefficient = allocate_shared_float(nz, nr, n_species) + constraints_C_coefficient = allocate_shared_float(nz, nr, n_species) + else + constraints_A_coefficient = nothing + constraints_B_coefficient = nothing + constraints_C_coefficient = nothing + end + # return struct containing arrays needed to update moments - return moments_charged_substruct(density, density_updated, parallel_flow, + return moments_ion_substruct(density, density_updated, parallel_flow, parallel_flow_updated, parallel_pressure, parallel_pressure_updated,perpendicular_pressure, parallel_heat_flux, parallel_heat_flux_updated, thermal_speed, chodura_integral_lower, chodura_integral_upper, v_norm_fac, @@ -345,7 +191,8 @@ function create_moments_charged(nz, nr, n_species, evolve_density, evolve_upar, dppar_dz, dppar_dz_upwind, d2ppar_dz2, dqpar_dz, dvth_dz, entropy_production, external_source_amplitude, external_source_density_amplitude, external_source_momentum_amplitude, external_source_pressure_amplitude, - external_source_controller_integral) + external_source_controller_integral, constraints_A_coefficient, + constraints_B_coefficient, constraints_C_coefficient) end # neutral particles have natural mean velocities @@ -355,7 +202,7 @@ end function create_moments_neutral(nz, nr, n_species, evolve_density, evolve_upar, evolve_ppar, neutral_source_settings, - numerical_dissipation) + num_diss_params) density = allocate_shared_float(nz, nr, n_species) density_updated = allocate_bool(n_species) density_updated .= false @@ -398,8 +245,7 @@ function create_moments_neutral(nz, nr, n_species, evolve_density, evolve_upar, ddens_dz = nothing ddens_dz_upwind = nothing end - if evolve_density && - numerical_dissipation.moment_dissipation_coefficient > 0.0 + if evolve_density && num_diss_params.neutral.moment_dissipation_coefficient > 0.0 d2dens_dz2 = allocate_shared_float(nz, nr, n_species) else @@ -415,8 +261,7 @@ function create_moments_neutral(nz, nr, n_species, evolve_density, evolve_upar, else duz_dz_upwind = nothing end - if evolve_upar && - numerical_dissipation.moment_dissipation_coefficient > 0.0 + if evolve_upar && num_diss_params.neutral.moment_dissipation_coefficient > 0.0 d2uz_dz2 = allocate_shared_float(nz, nr, n_species) else @@ -474,6 +319,16 @@ function create_moments_neutral(nz, nr, n_species, evolve_density, evolve_upar, external_source_controller_integral = allocate_shared_float(1, 1) end + if evolve_density || evolve_upar || evolve_ppar + constraints_A_coefficient = allocate_shared_float(nz, nr, n_species) + constraints_B_coefficient = allocate_shared_float(nz, nr, n_species) + constraints_C_coefficient = allocate_shared_float(nz, nr, n_species) + else + constraints_A_coefficient = nothing + constraints_B_coefficient = nothing + constraints_C_coefficient = 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, @@ -481,7 +336,8 @@ function create_moments_neutral(nz, nr, n_species, evolve_density, evolve_upar, d2dens_dz2, duz_dz, duz_dz_upwind, d2uz_dz2, dpz_dz, dpz_dz_upwind, d2pz_dz2, dqz_dz, dvth_dz, external_source_amplitude, external_source_density_amplitude, external_source_momentum_amplitude, external_source_pressure_amplitude, - external_source_controller_integral) + external_source_controller_integral, constraints_A_coefficient, + constraints_B_coefficient, constraints_C_coefficient) end """ @@ -492,7 +348,7 @@ the function used to update moments at run time is update_derived_moments! in ti function update_moments!(moments, ff_in, gyroavs::gyro_operators, vpa, vperp, z, r, composition, r_spectral, geometry, scratch_dummy, z_advect) if composition.gyrokinetic_ions - ff = scratch_dummy.buffer_vpavperpzrs_1 # the buffer array for the charged pdf -> make sure not to reuse this array below + ff = scratch_dummy.buffer_vpavperpzrs_1 # the buffer array for the ion pdf -> make sure not to reuse this array below # fill buffer with ring-averaged F (gyroaverage at fixed position) gyroaverage_pdf!(ff,ff_in,gyroavs,vpa,vperp,z,r,composition) else @@ -500,46 +356,46 @@ function update_moments!(moments, ff_in, gyroavs::gyro_operators, vpa, vperp, z, end begin_s_r_z_region() n_species = size(ff,5) - @boundscheck n_species == size(moments.charged.dens,3) || throw(BoundsError(moments)) + @boundscheck n_species == size(moments.ion.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], + if moments.ion.dens_updated[is] == false + @views update_density_species!(moments.ion.dens[:,:,is], ff[:,:,:,:,is], vpa, vperp, z, r) - moments.charged.dens_updated[is] = true + moments.ion.dens_updated[is] = true end - if moments.charged.upar_updated[is] == false + if moments.ion.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, + @views update_upar_species!(moments.ion.upar[:,:,is], + moments.ion.dens[:,:,is], + moments.ion.ppar[:,:,is], ff[:,:,:,:,is], vpa, vperp, z, r, moments.evolve_density, moments.evolve_ppar) - moments.charged.upar_updated[is] = true + moments.ion.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, + if moments.ion.ppar_updated[is] == false + @views update_ppar_species!(moments.ion.ppar[:,:,is], + moments.ion.dens[:,:,is], + moments.ion.upar[:,:,is], ff[:,:,:,:,is], vpa, vperp, z, r, moments.evolve_density, moments.evolve_upar) - moments.charged.ppar_updated[is] = true - end - @views update_pperp_species!(moments.charged.pperp[:,:,is], ff[:,:,:,:,is], vpa, vperp, z, r) - 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, + moments.ion.ppar_updated[is] = true + end + @views update_pperp_species!(moments.ion.pperp[:,:,is], ff[:,:,:,:,is], vpa, vperp, z, r) + if moments.ion.qpar_updated[is] == false + @views update_qpar_species!(moments.ion.qpar[:,:,is], + moments.ion.dens[:,:,is], + moments.ion.upar[:,:,is], + moments.ion.vth[:,:,is], ff[:,:,:,:,is], vpa, vperp, z, r, moments.evolve_density, moments.evolve_upar, moments.evolve_ppar) - moments.charged.qpar_updated[is] = true + moments.ion.qpar_updated[is] = true end end - update_vth!(moments.charged.vth, moments.charged.ppar, moments.charged.pperp, moments.charged.dens, vperp, z, r, composition) + update_vth!(moments.ion.vth, moments.ion.ppar, moments.ion.pperp, moments.ion.dens, vperp, z, r, composition) # update the Chodura diagnostic -- note that the pdf should be the unnormalised one # so this will break for the split moments cases update_chodura!(moments,ff,vpa,vperp,z,r,r_spectral,composition,geometry,scratch_dummy,z_advect) @@ -884,22 +740,22 @@ function update_chodura!(moments,ff,vpa,vperp,z,r,r_spectral,composition,geometr 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[:,:],vpa,vperp,z,r,composition,geometry,z_advect[is].speed[1,:,:,ir],moments.charged.dens[1,ir,is],del_vpa,1,ir) + @views moments.ion.chodura_integral_lower[ir,is] = update_chodura_integral_species!(ff[:,:,1,ir,is],dffdr[:,:,1,ir,is], + ff_dummy[:,:],vpa,vperp,z,r,composition,geometry,z_advect[is].speed[1,:,:,ir],moments.ion.dens[1,ir,is],del_vpa,1,ir) 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 + moments.ion.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[:,:],vpa,vperp,z,r,composition,geometry,z_advect[is].speed[end,:,:,ir],moments.charged.dens[end,ir,is],del_vpa,z.n,ir) + @views moments.ion.chodura_integral_upper[ir,is] = update_chodura_integral_species!(ff[:,:,end,ir,is],dffdr[:,:,end,ir,is], + ff_dummy[:,:],vpa,vperp,z,r,composition,geometry,z_advect[is].speed[end,:,:,ir],moments.ion.dens[end,ir,is],del_vpa,z.n,ir) 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 + moments.ion.chodura_integral_upper[ir,is] = 0.0 end end end @@ -947,15 +803,15 @@ 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) +function calculate_ion_moment_derivatives!(moments, scratch, scratch_dummy, z, z_spectral, + ion_mom_diss_coeff) begin_s_r_region() density = scratch.density upar = scratch.upar ppar = scratch.ppar - qpar = moments.charged.qpar - vth = moments.charged.vth + qpar = moments.ion.qpar + vth = moments.ion.vth dummy_zrs = scratch_dummy.dummy_zrs buffer_r_1 = scratch_dummy.buffer_rs_1 buffer_r_2 = scratch_dummy.buffer_rs_2 @@ -964,27 +820,26 @@ function calculate_moment_derivatives!(moments, scratch, scratch_dummy, z, z_spe buffer_r_5 = scratch_dummy.buffer_rs_5 buffer_r_6 = scratch_dummy.buffer_rs_6 if moments.evolve_density - @views derivative_z!(moments.charged.ddens_dz, density, buffer_r_1, + @views derivative_z!(moments.ion.ddens_dz, density, buffer_r_1, buffer_r_2, buffer_r_3, buffer_r_4, z_spectral, z) # Upwinded using upar as advection velocity, to be used in continuity equation @loop_s_r_z is ir iz begin dummy_zrs[iz,ir,is] = -upar[iz,ir,is] end - @views derivative_z!(moments.charged.ddens_dz_upwind, density, + @views derivative_z!(moments.ion.ddens_dz_upwind, density, dummy_zrs, 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 + if moments.evolve_density && ion_mom_diss_coeff > 0.0 # centred second derivative for dissipation @views derivative_z!(dummy_zrs, density, buffer_r_1, buffer_r_2, buffer_r_3, buffer_r_4, z_spectral, z) - @views derivative_z!(moments.charged.d2dens_dz2, dummy_zrs, buffer_r_1, + @views derivative_z!(moments.ion.d2dens_dz2, dummy_zrs, 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, upar, buffer_r_1, + @views derivative_z!(moments.ion.dupar_dz, upar, buffer_r_1, buffer_r_2, buffer_r_3, buffer_r_4, z_spectral, z) end if moments.evolve_upar @@ -993,21 +848,20 @@ function calculate_moment_derivatives!(moments, scratch, scratch_dummy, z, z_spe @loop_s_r_z is ir iz begin dummy_zrs[iz,ir,is] = -upar[iz,ir,is] end - @views derivative_z!(moments.charged.dupar_dz_upwind, upar, dummy_zrs, + @views derivative_z!(moments.ion.dupar_dz_upwind, upar, dummy_zrs, 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 + if moments.evolve_upar && ion_mom_diss_coeff > 0.0 # centred second derivative for dissipation @views derivative_z!(dummy_zrs, upar, buffer_r_1, buffer_r_2, buffer_r_3, buffer_r_4, z_spectral, z) - @views derivative_z!(moments.charged.d2upar_dz2, dummy_zrs, buffer_r_1, + @views derivative_z!(moments.ion.d2upar_dz2, dummy_zrs, 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, ppar, buffer_r_1, + @views derivative_z!(moments.ion.dppar_dz, ppar, buffer_r_1, buffer_r_2, buffer_r_3, buffer_r_4, z_spectral, z) end if moments.evolve_ppar @@ -1015,19 +869,19 @@ function calculate_moment_derivatives!(moments, scratch, scratch_dummy, z, z_spe @loop_s_r_z is ir iz begin dummy_zrs[iz,ir,is] = -upar[iz,ir,is] end - @views derivative_z!(moments.charged.dppar_dz_upwind, ppar, dummy_zrs, + @views derivative_z!(moments.ion.dppar_dz_upwind, ppar, dummy_zrs, 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_zrs, ppar, buffer_r_1, buffer_r_2, buffer_r_3, buffer_r_4, z_spectral, z) - @views derivative_z!(moments.charged.d2ppar_dz2, dummy_zrs, buffer_r_1, + @views derivative_z!(moments.ion.d2ppar_dz2, dummy_zrs, buffer_r_1, buffer_r_2, buffer_r_3, buffer_r_4, z_spectral, z) - @views derivative_z!(moments.charged.dqpar_dz, qpar, buffer_r_1, + @views derivative_z!(moments.ion.dqpar_dz, qpar, buffer_r_1, buffer_r_2, buffer_r_3, buffer_r_4, z_spectral, z) - @views derivative_z!(moments.charged.dvth_dz, vth, buffer_r_1, + @views derivative_z!(moments.ion.dvth_dz, vth, buffer_r_1, buffer_r_2, buffer_r_3, buffer_r_4, z_spectral, z) end end @@ -1506,8 +1360,8 @@ end Pre-calculate spatial derivatives of the neutral moments that will be needed for the time advance """ -function calculate_moment_derivatives_neutral!(moments, scratch, scratch_dummy, z, - z_spectral, numerical_dissipation) +function calculate_neutral_moment_derivatives!(moments, scratch, scratch_dummy, z, + z_spectral, neutral_mom_diss_coeff) begin_sn_r_region() density = scratch.density_neutral @@ -1534,8 +1388,7 @@ function calculate_moment_derivatives_neutral!(moments, scratch, scratch_dummy, dummy_zrsn, buffer_r_1, buffer_r_2, buffer_r_3, buffer_r_4, buffer_r_5, buffer_r_6, z_spectral, z; neutrals=true) end - if moments.evolve_density && - numerical_dissipation.moment_dissipation_coefficient > 0.0 + if moments.evolve_density && neutral_mom_diss_coeff > 0.0 # centred second derivative for dissipation @views derivative_z!(dummy_zrsn, density, buffer_r_1, buffer_r_2, buffer_r_3, @@ -1559,8 +1412,7 @@ function calculate_moment_derivatives_neutral!(moments, scratch, scratch_dummy, buffer_r_1, buffer_r_2, buffer_r_3, buffer_r_4, buffer_r_5, buffer_r_6, z_spectral, z; neutrals=true) end - if moments.evolve_upar && - numerical_dissipation.moment_dissipation_coefficient > 0.0 + if moments.evolve_upar && neutral_mom_diss_coeff > 0.0 # centred second derivative for dissipation @views derivative_z!(dummy_zrsn, uz, buffer_r_1, buffer_r_2, buffer_r_3, @@ -1800,18 +1652,18 @@ end """ function reset_moments_status!(moments) if moments.evolve_density == false - moments.charged.dens_updated .= false + moments.ion.dens_updated .= false moments.neutral.dens_updated .= false end if moments.evolve_upar == false - moments.charged.upar_updated .= false + moments.ion.upar_updated .= false moments.neutral.uz_updated .= false end if moments.evolve_ppar == false - moments.charged.ppar_updated .= false + moments.ion.ppar_updated .= false moments.neutral.pz_updated .= false end - moments.charged.qpar_updated .= false + moments.ion.qpar_updated .= false moments.neutral.uzeta_updated .= false moments.neutral.ur_updated .= false moments.neutral.pzeta_updated .= false diff --git a/moment_kinetics/src/vpa_advection.jl b/moment_kinetics/src/vpa_advection.jl index ed8afd2fa..9d4881b0b 100644 --- a/moment_kinetics/src/vpa_advection.jl +++ b/moment_kinetics/src/vpa_advection.jl @@ -113,9 +113,9 @@ function update_speed_n_u_p_evolution!(advect, fvec, moments, vpa, z, r, composi # • -wpar^2 * d(vth)/dz term @loop_z_vperp iz ivperp begin @views @. advect[is].speed[:,ivperp,iz,ir] = - moments.charged.dppar_dz[iz,ir,is]/(fvec.density[iz,ir,is]*moments.charged.vth[iz,ir,is]) + - 0.5*vpa.grid*moments.charged.dqpar_dz[iz,ir,is]/fvec.ppar[iz,ir,is] - - vpa.grid^2*moments.charged.dvth_dz[iz,ir,is] + moments.ion.dppar_dz[iz,ir,is]/(fvec.density[iz,ir,is]*moments.ion.vth[iz,ir,is]) + + 0.5*vpa.grid*moments.ion.dqpar_dz[iz,ir,is]/fvec.ppar[iz,ir,is] - + vpa.grid^2*moments.ion.dvth_dz[iz,ir,is] end end end @@ -134,7 +134,7 @@ function update_speed_n_u_p_evolution!(advect, fvec, moments, vpa, z, r, composi * (fvec.upar[iz,ir,is]-fvec.uz_neutral[iz,ir,is])^2) - fvec.density_neutral[iz,ir,is] * (fvec.uz_neutral[iz,ir,is]-fvec.upar[iz,ir,is]) - / moments.charged.vth[iz,ir,is]) + + / moments.ion.vth[iz,ir,is]) + collisions.ionization * (0.5*vpa.grid * (fvec.density_neutral[iz,ir,is] @@ -145,24 +145,26 @@ function update_speed_n_u_p_evolution!(advect, fvec, moments, vpa, z, r, composi / fvec.ppar[iz,ir,is]) - fvec.density_neutral[iz,ir,is] * (fvec.uz_neutral[iz,ir,is] - fvec.upar[iz,ir,is]) - / moments.charged.vth[iz,ir,is]) + / moments.ion.vth[iz,ir,is]) end end end if ion_source_settings.active - source_amplitude = moments.charged.external_source_amplitude - source_T = ion_source_settings.source_T + source_density_amplitude = moments.ion.external_source_density_amplitude + source_momentum_amplitude = moments.ion.external_source_momentum_amplitude + source_pressure_amplitude = moments.ion.external_source_pressure_amplitude density = fvec.density upar = fvec.upar ppar = fvec.ppar - vth = moments.charged.vth + vth = moments.ion.vth vpa_grid = vpa.grid @loop_s_r_z is ir iz begin - prefactor = source_amplitude[iz,ir] - term1 = prefactor * upar[iz,ir,is]/(density[iz,ir,is]*vth[iz,ir,is]) + term1 = source_density_amplitude[iz,ir] * upar[iz,ir,is]/(density[iz,ir,is]*vth[iz,ir,is]) term2_over_vpa = - 0.5 * prefactor * (-(0.5*source_T + upar[iz,ir,is]^2) / ppar[iz,ir,is] - + 1.0/density[iz,ir,is]) + -0.5 * (source_pressure_amplitude[iz,ir] + + 2.0 * upar[iz,ir,is] * source_momentum_amplitude[iz,ir]) / + ppar[iz,ir,is] + + 0.5 * source_density_amplitude[iz,ir] / density[iz,ir,is] @loop_vperp_vpa ivperp ivpa begin advect[is].speed[ivpa,ivperp,iz,ir] += term1 + vpa_grid[ivpa] * term2_over_vpa end @@ -187,9 +189,9 @@ function update_speed_n_p_evolution!(advect, fields, fvec, moments, vpa, z, r, # • vpahat*d(upar)/dz # • -(1/2)*(dphi/dz)/vthi @loop_z_vperp iz ivperp begin - @views @. advect[is].speed[:,ivperp,iz,ir] = 0.5*vpa.grid*moments.charged.dqpar_dz[iz,ir,is]/fvec.ppar[iz,ir,is] + - vpa.grid*moments.charged.dvth_dz[iz] * (fvec.upar[iz,ir,is]/moments.vth[iz,ir,is] - vpa.grid) + - vpa.grid*moments.charged.dupar_dz[iz,ir,is] + + @views @. advect[is].speed[:,ivperp,iz,ir] = 0.5*vpa.grid*moments.ion.dqpar_dz[iz,ir,is]/fvec.ppar[iz,ir,is] + + vpa.grid*moments.ion.dvth_dz[iz] * (fvec.upar[iz,ir,is]/moments.vth[iz,ir,is] - vpa.grid) + + vpa.grid*moments.ion.dupar_dz[iz,ir,is] + 0.5*fields.Ez[iz,ir]/moments.vth[iz,ir,is] end end @@ -228,8 +230,8 @@ function update_speed_n_u_evolution!(advect, fvec, moments, vpa, z, r, compositi # • -wpar*dupar/dz @loop_z_vperp iz ivperp begin @views @. advect[is].speed[:,ivperp,iz,ir] = - moments.charged.dppar_dz[iz,ir,is]/fvec.density[iz,ir,is] - - vpa.grid*moments.charged.dupar_dz[iz,ir,is] + moments.ion.dppar_dz[iz,ir,is]/fvec.density[iz,ir,is] - + vpa.grid*moments.ion.dupar_dz[iz,ir,is] end end end @@ -253,16 +255,15 @@ function update_speed_n_u_evolution!(advect, fvec, moments, vpa, z, r, compositi end end if ion_source_settings.active - source_amplitude = moments.charged.external_source_amplitude + source_density_amplitude = moments.ion.external_source_density_amplitude source_strength = ion_source_settings.source_strength - source_T = ion_source_settings.source_T r_amplitude = ion_source_settings.r_amplitude z_amplitude = ion_source_settings.z_amplitude density = fvec.density upar = fvec.upar - vth = moments.charged.vth + vth = moments.ion.vth @loop_s_r_z is ir iz begin - term = source_amplitude[iz,ir] * upar[iz,ir,is] / density[iz,ir,is] + term = source_density_amplitude[iz,ir] * upar[iz,ir,is] / density[iz,ir,is] @loop_vperp_vpa ivperp ivpa begin advect[is].speed[ivpa,ivperp,iz,ir] += term end diff --git a/moment_kinetics/src/z_advection.jl b/moment_kinetics/src/z_advection.jl index b88808a3e..9013dd19e 100644 --- a/moment_kinetics/src/z_advection.jl +++ b/moment_kinetics/src/z_advection.jl @@ -21,13 +21,13 @@ function z_advection!(f_out, fvec_in, moments, fields, advect, z, vpa, vperp, r, @loop_s is begin # get the updated speed along the z direction using the current f @views update_speed_z!(advect[is], fvec_in.upar[:,:,is], - moments.charged.vth[:,:,is], moments.evolve_upar, + moments.ion.vth[:,:,is], moments.evolve_upar, moments.evolve_ppar, fields, vpa, vperp, z, r, t, geometry, is) # update adv_fac @loop_r_vperp_vpa ir ivperp ivpa begin @views adjust_advection_speed!(advect[is].speed[:,ivpa,ivperp,ir], fvec_in.density[:,ir,is], - moments.charged.vth[:,ir,is], + moments.ion.vth[:,ir,is], moments.evolve_density, moments.evolve_ppar) @views @. advect[is].adv_fac[:,ivpa,ivperp,ir] = -dt*advect[is].speed[:,ivpa,ivperp,ir] # take the normalized pdf contained in fvec_in.pdf and remove the normalization, @@ -35,7 +35,7 @@ function z_advection!(f_out, fvec_in, moments, fields, advect, z, vpa, vperp, r, @views unnormalize_pdf!( scratch_dummy.buffer_vpavperpzrs_2[ivpa,ivperp,:,ir,is], fvec_in.pdf[ivpa,ivperp,:,ir,is], fvec_in.density[:,ir,is], - moments.charged.vth[:,ir,is], moments.evolve_density, moments.evolve_ppar) + moments.ion.vth[:,ir,is], moments.evolve_density, moments.evolve_ppar) end end #calculate the upwind derivative diff --git a/moment_kinetics/test/Krook_collisions_tests.jl b/moment_kinetics/test/Krook_collisions_tests.jl index 4f273049d..91e6bb7de 100644 --- a/moment_kinetics/test/Krook_collisions_tests.jl +++ b/moment_kinetics/test/Krook_collisions_tests.jl @@ -10,7 +10,7 @@ using moment_kinetics.coordinates: define_coordinate using moment_kinetics.input_structs: grid_input, advection_input using moment_kinetics.load_data: open_readonly_output_file, load_coordinate_data, load_species_data, load_fields_data, - load_charged_particle_moments_data, load_pdf_data, + load_ion_moments_data, load_pdf_data, load_neutral_particle_moments_data, load_neutral_pdf_data, load_time_data, load_species_data using moment_kinetics.interpolation: interpolate_to_grid_z, interpolate_to_grid_vpa @@ -35,48 +35,48 @@ const expected = -0.353459763648872 -0.3755795658392631; -0.5494724768120175 -0.5904511161957432; -0.8609860698164502 -0.8726509017405427; -1.2115129555832849 -1.1306145497660032; -1.3862820803244258 -1.2382381134436695], - n_charged=[0.2500030702177186 0.28989452952580286; 0.2977473631375158 0.32283464906590775; - 0.42274585818529853 0.41784232850006636; 0.5772542465450629 0.5540775204094593; - 0.7022542481909738 0.6868914177534788; 0.7499999999999394 0.7468272160708606; - 0.7022542481909738 0.6868914177534787; 0.577254246545063 0.554077520409459; - 0.42274585818529864 0.4178423285000665; 0.2977473631375159 0.3228346490659078; - 0.2500030702177185 0.2898945295258028], + n_ion=[0.2500030702177186 0.28989452952580286; 0.2977473631375158 0.32283464906590775; + 0.42274585818529853 0.41784232850006636; 0.5772542465450629 0.5540775204094593; + 0.7022542481909738 0.6868914177534788; 0.7499999999999394 0.7468272160708606; + 0.7022542481909738 0.6868914177534787; 0.577254246545063 0.554077520409459; + 0.42274585818529864 0.4178423285000665; 0.2977473631375159 0.3228346490659078; + 0.2500030702177185 0.2898945295258028], n_neutral=[0.7499999999999382 0.7736770941648626; 0.7022542481909748 0.7056867075427516; 0.5772542465450632 0.5582975660019874; 0.4227458581852985 0.4096913953484598; 0.29774736313751604 0.3053964124252619; 0.2500030702177186 0.2681998023548167; 0.29774736313751604 0.3053964124252619; 0.42274585818529836 0.4096913953484599; 0.5772542465450631 0.5582975660019875; 0.7022542481909745 0.7056867075427524; 0.7499999999999383 0.7736770941648626], - upar_charged=[-2.7135787559953277e-17 -1.6845791254993525e-16; -9.321028970172899e-18 -0.18245939812953485; - -2.8374879811351724e-18 -0.19666454846377826; 1.2124327390522635e-17 -0.11128043369942339; - 3.6525788403693063e-17 -0.03317985705380149; -2.0930856430671915e-17 4.720175801869314e-17; - 8.753545920086251e-18 0.033179857053801595; 1.1293771270243255e-17 0.11128043369942343; - 1.3739171132886587e-17 0.19666454846377784; -6.840453743089351e-18 0.18245939812953468; - -2.7135787559953277e-17 -1.9129596434811267e-16], + upar_ion=[-2.7135787559953277e-17 -1.6845791254993525e-16; -9.321028970172899e-18 -0.18245939812953485; + -2.8374879811351724e-18 -0.19666454846377826; 1.2124327390522635e-17 -0.11128043369942339; + 3.6525788403693063e-17 -0.03317985705380149; -2.0930856430671915e-17 4.720175801869314e-17; + 8.753545920086251e-18 0.033179857053801595; 1.1293771270243255e-17 0.11128043369942343; + 1.3739171132886587e-17 0.19666454846377784; -6.840453743089351e-18 0.18245939812953468; + -2.7135787559953277e-17 -1.9129596434811267e-16], upar_neutral=[6.5569385065066925e-18 8.08747058038406e-18; 1.1054500872839027e-17 -0.03620988455458174; -3.241833393685864e-17 -0.009156078199383568; -3.617637280460899e-17 0.05452623197292568; 4.417578961284041e-17 0.07607875911384775; 4.9354467746194965e-17 1.635044638743921e-16; 6.573091229872379e-18 -0.0760787591138477; 2.989662686945165e-17 -0.05452623197292564; -3.1951996361666834e-17 0.009156078199383685; -4.395464518158184e-18 0.03620988455458165; 6.5569385065066925e-18 1.8232586069007834e-18], - ppar_charged=[0.18749999999999992 0.23302732230115558; 0.20909325514551116 0.21936799130257528; - 0.24403180771238264 0.20856296024163393; 0.24403180771238278 0.2154266357557397; - 0.2090932551455113 0.2206183912107678; 0.1875 0.21979739387340663; - 0.20909325514551128 0.22061839121076784; 0.2440318077123828 0.21542663575573945; - 0.24403180771238256 0.20856296024163395; 0.20909325514551116 0.2193679913025754; - 0.18749999999999992 0.23302732230115553], + ppar_ion=[0.18749999999999992 0.23302732230115558; 0.20909325514551116 0.21936799130257528; + 0.24403180771238264 0.20856296024163393; 0.24403180771238278 0.2154266357557397; + 0.2090932551455113 0.2206183912107678; 0.1875 0.21979739387340663; + 0.20909325514551128 0.22061839121076784; 0.2440318077123828 0.21542663575573945; + 0.24403180771238256 0.20856296024163395; 0.20909325514551116 0.2193679913025754; + 0.18749999999999992 0.23302732230115553], ppar_neutral=[0.18750000000000003 0.2480292382671593; 0.20909325514551122 0.24401255100297964; 0.24403180771238286 0.22861763406831279; 0.24403180771238278 0.2058922545451891; 0.20909325514551144 0.1926313699453636; 0.18749999999999992 0.19090651730415983; 0.20909325514551141 0.19263136994536365; 0.2440318077123828 0.20589225454518903; 0.24403180771238286 0.2286176340683127; 0.20909325514551114 0.24401255100297964; 0.18750000000000006 0.24802923826715936], - f_charged=[0.0370462360994826 0.04059927063892091 0.0428431419871786 0.030398267195914062 0.01236045902698859 0.006338529470383425 0.012360459026988587 0.030398267195914028 0.04284314198717859 0.0405992706389209 0.0370462360994826; - 0.20411991941198782 0.25123395823993105 0.3934413727192304 0.6277900619432855 0.9100364506661008 1.0606601717796504 0.910036450666101 0.6277900619432859 0.39344137271923046 0.25123395823993094 0.20411991941198776; - 0.0370462360994826 0.04059927063892091 0.0428431419871786 0.030398267195914062 0.01236045902698859 0.006338529470383425 0.012360459026988587 0.030398267195914028 0.04284314198717859 0.0405992706389209 0.0370462360994826;;; - 0.0538996852594264 0.06066864433237418 0.03746866696438989 0.014783440166032301 0.010917691665145668 0.018422971878502774 0.027170953411068444 0.027269146560166702 0.026567569739750264 0.035612674100528624 0.05389968525942639; - 0.2118369019176154 0.24917436308523389 0.37345448114678914 0.5972219245577428 0.8859681860177208 1.0485988935814787 0.8859681860177204 0.5972219245577435 0.37345448114678825 0.24917436308523389 0.2118369019176155; - 0.05389968525942635 0.03561267410052869 0.02656756973975021 0.02726914656016675 0.027170953411068514 0.018422971878502753 0.01091769166514568 0.014783440166032254 0.037468666964389795 0.060668644332374164 0.05389968525942635], + f_ion=[0.0370462360994826 0.04059927063892091 0.0428431419871786 0.030398267195914062 0.01236045902698859 0.006338529470383425 0.012360459026988587 0.030398267195914028 0.04284314198717859 0.0405992706389209 0.0370462360994826; + 0.20411991941198782 0.25123395823993105 0.3934413727192304 0.6277900619432855 0.9100364506661008 1.0606601717796504 0.910036450666101 0.6277900619432859 0.39344137271923046 0.25123395823993094 0.20411991941198776; + 0.0370462360994826 0.04059927063892091 0.0428431419871786 0.030398267195914062 0.01236045902698859 0.006338529470383425 0.012360459026988587 0.030398267195914028 0.04284314198717859 0.0405992706389209 0.0370462360994826;;; + 0.0538996852594264 0.06066864433237418 0.03746866696438989 0.014783440166032301 0.010917691665145668 0.018422971878502774 0.027170953411068444 0.027269146560166702 0.026567569739750264 0.035612674100528624 0.05389968525942639; + 0.2118369019176154 0.24917436308523389 0.37345448114678914 0.5972219245577428 0.8859681860177208 1.0485988935814787 0.8859681860177204 0.5972219245577435 0.37345448114678825 0.24917436308523389 0.2118369019176155; + 0.05389968525942635 0.03561267410052869 0.02656756973975021 0.02726914656016675 0.027170953411068514 0.018422971878502753 0.01091769166514568 0.014783440166032254 0.037468666964389795 0.060668644332374164 0.05389968525942635], f_neutral=[0.0063385294703834595 0.012360459026988546 0.030398267195914108 0.04284314198717859 0.040599270638920985 0.03704623609948259 0.040599270638920965 0.0428431419871786 0.030398267195914094 0.012360459026988546 0.006338529470383456; 1.0606601717796493 0.9100364506661016 0.6277900619432857 0.3934413727192303 0.2512339582399308 0.20411991941198754 0.2512339582399307 0.3934413727192301 0.6277900619432853 0.9100364506661016 1.0606601717796487; 0.0063385294703834595 0.012360459026988546 0.030398267195914108 0.04284314198717859 0.040599270638920985 0.03704623609948259 0.040599270638920965 0.0428431419871786 0.030398267195914094 0.012360459026988546 0.006338529470383456;;; @@ -164,6 +164,10 @@ function run_test(test_input, rtol, atol; args...) # by passing keyword arguments to run_test, args becomes a Dict which can be used to # update the default inputs + # Make a copy to make sure nothing modifies the input Dicts defined in this test + # script. + test_input = deepcopy(test_input) + # Convert keyword arguments to a unique name name = test_input["run_name"] if length(args) > 0 @@ -191,10 +195,10 @@ function run_test(test_input, rtol, atol; args...) end phi = nothing - n_charged = nothing - upar_charged = nothing - ppar_charged = nothing - f_charged = nothing + n_ion = nothing + upar_ion = nothing + ppar_ion = nothing + f_ion = nothing n_neutral = nothing upar_neutral = nothing ppar_neutral = nothing @@ -222,7 +226,7 @@ function run_test(test_input, rtol, atol; args...) phi_zrt, Er_zrt, Ez_zrt = load_fields_data(fid) # load velocity moments data - n_charged_zrst, upar_charged_zrst, ppar_charged_zrst, qpar_charged_zrst, v_t_charged_zrst = load_charged_particle_moments_data(fid) + n_ion_zrst, upar_ion_zrst, ppar_ion_zrst, qpar_ion_zrst, v_t_ion_zrst = load_ion_moments_data(fid) n_neutral_zrst, upar_neutral_zrst, ppar_neutral_zrst, qpar_neutral_zrst, v_t_neutral_zrst = load_neutral_particle_moments_data(fid) z, z_spectral = load_coordinate_data(fid, "z") @@ -232,19 +236,19 @@ function run_test(test_input, rtol, atol; args...) fid = open_readonly_output_file(path, "dfns") # load particle distribution function (pdf) data - f_charged_vpavperpzrst = load_pdf_data(fid) + f_ion_vpavperpzrst = load_pdf_data(fid) f_neutral_vzvrvzetazrst = load_neutral_pdf_data(fid) vpa, vpa_spectral = load_coordinate_data(fid, "vpa") close(fid) phi = phi_zrt[:,1,:] - n_charged = n_charged_zrst[:,1,:,:] - upar_charged = upar_charged_zrst[:,1,:,:] - ppar_charged = ppar_charged_zrst[:,1,:,:] - qpar_charged = qpar_charged_zrst[:,1,:,:] - v_t_charged = v_t_charged_zrst[:,1,:,:] - f_charged = f_charged_vpavperpzrst[:,1,:,1,:,:] + n_ion = n_ion_zrst[:,1,:,:] + upar_ion = upar_ion_zrst[:,1,:,:] + ppar_ion = ppar_ion_zrst[:,1,:,:] + qpar_ion = qpar_ion_zrst[:,1,:,:] + v_t_ion = v_t_ion_zrst[:,1,:,:] + f_ion = f_ion_vpavperpzrst[:,1,:,1,:,:] n_neutral = n_neutral_zrst[:,1,:,:] upar_neutral = upar_neutral_zrst[:,1,:,:] ppar_neutral = ppar_neutral_zrst[:,1,:,:] @@ -255,7 +259,7 @@ function run_test(test_input, rtol, atol; args...) # Unnormalize f if input["evolve_moments_density"] for it ∈ 1:length(time), is ∈ 1:n_ion_species, iz ∈ 1:z.n - f_charged[:,iz,is,it] .*= n_charged[iz,is,it] + f_ion[:,iz,is,it] .*= n_ion[iz,is,it] end for it ∈ 1:length(time), isn ∈ 1:n_neutral_species, iz ∈ 1:z.n f_neutral[:,iz,isn,it] .*= n_neutral[iz,isn,it] @@ -263,7 +267,7 @@ function run_test(test_input, rtol, atol; args...) end if input["evolve_moments_parallel_pressure"] for it ∈ 1:length(time), is ∈ 1:n_ion_species, iz ∈ 1:z.n - f_charged[:,iz,is,it] ./= v_t_charged[iz,is,it] + f_ion[:,iz,is,it] ./= v_t_ion[iz,is,it] end for it ∈ 1:length(time), isn ∈ 1:n_neutral_species, iz ∈ 1:z.n f_neutral[:,iz,isn,it] ./= v_t_neutral[iz,isn,it] @@ -281,11 +285,11 @@ function run_test(test_input, rtol, atol; args...) #println("phi ", size(newgrid_phi)) #println(newgrid_phi) #println() - #newgrid_n_charged = cat(interpolate_to_grid_z(expected.z, n_charged[:, :, 1], z, z_spectral)[:,1], - # interpolate_to_grid_z(expected.z, n_charged[:, :, 2], z, z_spectral)[:,1]; + #newgrid_n_ion = cat(interpolate_to_grid_z(expected.z, n_ion[:, :, 1], z, z_spectral)[:,1], + # interpolate_to_grid_z(expected.z, n_ion[:, :, 2], z, z_spectral)[:,1]; # dims=2) - #println("n_charged ", size(newgrid_n_charged)) - #println(newgrid_n_charged) + #println("n_ion ", size(newgrid_n_ion)) + #println(newgrid_n_ion) #println() #newgrid_n_neutral = cat(interpolate_to_grid_z(expected.z, n_neutral[:, :, 1], z, z_spectral)[:,1], # interpolate_to_grid_z(expected.z, n_neutral[:, :, 2], z, z_spectral)[:,1]; @@ -293,11 +297,11 @@ function run_test(test_input, rtol, atol; args...) #println("n_neutral ", size(newgrid_n_neutral)) #println(newgrid_n_neutral) #println() - #newgrid_upar_charged = cat(interpolate_to_grid_z(expected.z, upar_charged[:, :, 1], z, z_spectral)[:,1], - # interpolate_to_grid_z(expected.z, upar_charged[:, :, 2], z, z_spectral)[:,1]; + #newgrid_upar_ion = cat(interpolate_to_grid_z(expected.z, upar_ion[:, :, 1], z, z_spectral)[:,1], + # interpolate_to_grid_z(expected.z, upar_ion[:, :, 2], z, z_spectral)[:,1]; # dims=2) - #println("upar_charged ", size(newgrid_upar_charged)) - #println(newgrid_upar_charged) + #println("upar_ion ", size(newgrid_upar_ion)) + #println(newgrid_upar_ion) #println() #newgrid_upar_neutral = cat(interpolate_to_grid_z(expected.z, upar_neutral[:, :, 1], z, z_spectral)[:,1], # interpolate_to_grid_z(expected.z, upar_neutral[:, :, 2], z, z_spectral)[:,1]; @@ -305,11 +309,11 @@ function run_test(test_input, rtol, atol; args...) #println("upar_neutral ", size(newgrid_upar_neutral)) #println(newgrid_upar_neutral) #println() - #newgrid_ppar_charged = cat(interpolate_to_grid_z(expected.z, ppar_charged[:, :, 1], z, z_spectral)[:,1], - # interpolate_to_grid_z(expected.z, ppar_charged[:, :, 2], z, z_spectral)[:,1]; + #newgrid_ppar_ion = cat(interpolate_to_grid_z(expected.z, ppar_ion[:, :, 1], z, z_spectral)[:,1], + # interpolate_to_grid_z(expected.z, ppar_ion[:, :, 2], z, z_spectral)[:,1]; # dims=2) - #println("ppar_charged ", size(newgrid_ppar_charged)) - #println(newgrid_ppar_charged) + #println("ppar_ion ", size(newgrid_ppar_ion)) + #println(newgrid_ppar_ion) #println() #newgrid_ppar_neutral = cat(interpolate_to_grid_z(expected.z, ppar_neutral[:, :, 1], z, z_spectral)[:,1], # interpolate_to_grid_z(expected.z, ppar_neutral[:, :, 2], z, z_spectral)[:,1]; @@ -317,11 +321,11 @@ function run_test(test_input, rtol, atol; args...) #println("ppar_neutral ", size(newgrid_ppar_neutral)) #println(newgrid_ppar_neutral) #println() - #newgrid_f_charged = cat(interpolate_to_grid_vpa(expected.vpa, interpolate_to_grid_z(expected.z, f_charged[:, :, :, 1], z, z_spectral), vpa, vpa_spectral)[:,:,1], - # interpolate_to_grid_vpa(expected.vpa, interpolate_to_grid_z(expected.z, f_charged[:, :, :, 2], z, z_spectral), vpa, vpa_spectral)[:,:,1]; + #newgrid_f_ion = cat(interpolate_to_grid_vpa(expected.vpa, interpolate_to_grid_z(expected.z, f_ion[:, :, :, 1], z, z_spectral), vpa, vpa_spectral)[:,:,1], + # interpolate_to_grid_vpa(expected.vpa, interpolate_to_grid_z(expected.z, f_ion[:, :, :, 2], z, z_spectral), vpa, vpa_spectral)[:,:,1]; # dims=4) - #println("f_charged ", size(newgrid_f_charged)) - #println(newgrid_f_charged) + #println("f_ion ", size(newgrid_f_ion)) + #println(newgrid_f_ion) #println() #newgrid_f_neutral = cat(interpolate_to_grid_vpa(expected.vpa, interpolate_to_grid_z(expected.z, f_neutral[:, :, :, 1], z, z_spectral), vpa, vpa_spectral)[:,:,1], # interpolate_to_grid_vpa(expected.vpa, interpolate_to_grid_z(expected.z, f_neutral[:, :, :, 2], z, z_spectral), vpa, vpa_spectral)[:,:,1]; @@ -334,36 +338,36 @@ function run_test(test_input, rtol, atol; args...) newgrid_phi = interpolate_to_grid_z(expected.z, phi[:, tind], z, z_spectral) @test isapprox(expected.phi[:, tind], newgrid_phi, rtol=rtol) - # Check charged particle moments and f + # Check ion particle moments and f ###################################### - newgrid_n_charged = interpolate_to_grid_z(expected.z, n_charged[:, :, tind], z, z_spectral) - @test isapprox(expected.n_charged[:, tind], newgrid_n_charged[:,1], rtol=rtol) + newgrid_n_ion = interpolate_to_grid_z(expected.z, n_ion[:, :, tind], z, z_spectral) + @test isapprox(expected.n_ion[:, tind], newgrid_n_ion[:,1], rtol=rtol) - newgrid_upar_charged = interpolate_to_grid_z(expected.z, upar_charged[:, :, tind], z, z_spectral) - @test isapprox(expected.upar_charged[:, tind], newgrid_upar_charged[:,1], rtol=rtol, atol=atol) + newgrid_upar_ion = interpolate_to_grid_z(expected.z, upar_ion[:, :, tind], z, z_spectral) + @test isapprox(expected.upar_ion[:, tind], newgrid_upar_ion[:,1], rtol=rtol, atol=atol) - newgrid_ppar_charged = interpolate_to_grid_z(expected.z, ppar_charged[:, :, tind], z, z_spectral) - @test isapprox(expected.ppar_charged[:, tind], newgrid_ppar_charged[:,1], rtol=rtol) + newgrid_ppar_ion = interpolate_to_grid_z(expected.z, ppar_ion[:, :, tind], z, z_spectral) + @test isapprox(expected.ppar_ion[:, tind], newgrid_ppar_ion[:,1], rtol=rtol) - newgrid_vth_charged = @. sqrt(2.0*newgrid_ppar_charged/newgrid_n_charged) - newgrid_f_charged = interpolate_to_grid_z(expected.z, f_charged[:, :, :, tind], z, z_spectral) - temp = newgrid_f_charged - newgrid_f_charged = fill(NaN, length(expected.vpa), - size(newgrid_f_charged, 2), - size(newgrid_f_charged, 3), - size(newgrid_f_charged, 4)) + newgrid_vth_ion = @. sqrt(2.0*newgrid_ppar_ion/newgrid_n_ion) + newgrid_f_ion = interpolate_to_grid_z(expected.z, f_ion[:, :, :, tind], z, z_spectral) + temp = newgrid_f_ion + newgrid_f_ion = fill(NaN, length(expected.vpa), + size(newgrid_f_ion, 2), + size(newgrid_f_ion, 3), + size(newgrid_f_ion, 4)) for iz ∈ 1:length(expected.z) wpa = copy(expected.vpa) if input["evolve_moments_parallel_flow"] - wpa .-= newgrid_upar_charged[iz,1] + wpa .-= newgrid_upar_ion[iz,1] end if input["evolve_moments_parallel_pressure"] - wpa ./= newgrid_vth_charged[iz,1] + wpa ./= newgrid_vth_ion[iz,1] end - newgrid_f_charged[:,iz,1] = interpolate_to_grid_vpa(wpa, temp[:,iz,1], vpa, vpa_spectral) + newgrid_f_ion[:,iz,1] = interpolate_to_grid_vpa(wpa, temp[:,iz,1], vpa, vpa_spectral) end - @test isapprox(expected.f_charged[:, :, tind], newgrid_f_charged[:,:,1], rtol=rtol) + @test isapprox(expected.f_ion[:, :, tind], newgrid_f_ion[:,:,1], rtol=rtol) # Check neutral particle moments and f ###################################### diff --git a/moment_kinetics/test/calculus_tests.jl b/moment_kinetics/test/calculus_tests.jl index da09fa0bc..52c0aee90 100644 --- a/moment_kinetics/test/calculus_tests.jl +++ b/moment_kinetics/test/calculus_tests.jl @@ -43,6 +43,8 @@ function runtests() discretization, fd_option, cheb_option, bc, adv_input, comm, element_spacing_option) # create the coordinate struct 'x' + # This test runs effectively in serial, so use `ignore_MPI=true` to avoid + # errors due to communicators not being fully set up. x, spectral = define_coordinate(input; ignore_MPI=true) # create array for the function f(x) to be differentiated/integrated f = Array{Float64,1}(undef, x.n) @@ -93,6 +95,8 @@ function runtests() "finite_difference", fd_option, cheb_option, bc, adv_input, comm, element_spacing_option) # create the coordinate struct 'x' + # This test runs effectively in serial, so use `ignore_MPI=true` to avoid + # errors due to communicators not being fully set up. x, spectral = define_coordinate(input; ignore_MPI=true) # create array for the derivative df/dx and the expected result @@ -143,6 +147,8 @@ function runtests() "finite_difference", fd_option, cheb_option, bc, adv_input, comm, element_spacing_option) # create the coordinate struct 'x' + # This test runs effectively in serial, so use `ignore_MPI=true` to avoid + # errors due to communicators not being fully set up. x, spectral = define_coordinate(input; ignore_MPI=true) # create array for the derivative df/dx and the expected result @@ -189,6 +195,8 @@ function runtests() "finite_difference", fd_option, cheb_option, bc, adv_input, comm, element_spacing_option) # create the coordinate struct 'x' + # This test runs effectively in serial, so use `ignore_MPI=true` to avoid + # errors due to communicators not being fully set up. x, spectral = define_coordinate(input; ignore_MPI=true) # create array for the derivative df/dx and the expected result @@ -243,6 +251,8 @@ function runtests() "finite_difference", fd_option, cheb_option, bc, adv_input, comm, element_spacing_option) # create the coordinate struct 'x' + # This test runs effectively in serial, so use `ignore_MPI=true` to avoid + # errors due to communicators not being fully set up. x, spectral = define_coordinate(input; ignore_MPI=true) # create array for the derivative df/dx and the expected result @@ -459,6 +469,8 @@ function runtests() "chebyshev_pseudospectral", fd_option, cheb_option, bc, adv_input, comm, element_spacing_option) # create the coordinate struct 'x' and info for derivatives, etc. + # This test runs effectively in serial, so use `ignore_MPI=true` to avoid + # errors due to communicators not being fully set up. x, spectral = define_coordinate(input; ignore_MPI=true) offset = randn(rng) @@ -655,6 +667,8 @@ function runtests() "chebyshev_pseudospectral", fd_option, cheb_option, bc, adv_input, comm, element_spacing_option) # create the coordinate struct 'x' and info for derivatives, etc. + # This test runs effectively in serial, so use `ignore_MPI=true` to avoid + # errors due to communicators not being fully set up. x, spectral = define_coordinate(input; ignore_MPI=true) offset = randn(rng) @@ -698,6 +712,8 @@ function runtests() "chebyshev_pseudospectral", fd_option, cheb_option, bc, adv_input, comm, element_spacing_option) # create the coordinate struct 'x' and info for derivatives, etc. + # This test runs effectively in serial, so use `ignore_MPI=true` to avoid + # errors due to communicators not being fully set up. x, spectral = define_coordinate(input; ignore_MPI=true) # test polynomials up to order ngrid-1 for n ∈ 0:ngrid-1 @@ -748,6 +764,8 @@ function runtests() "chebyshev_pseudospectral", fd_option, cheb_option, bc, adv_input, comm, element_spacing_option) # create the coordinate struct 'x' and info for derivatives, etc. + # This test runs effectively in serial, so use `ignore_MPI=true` to avoid + # errors due to communicators not being fully set up. x, spectral = define_coordinate(input; ignore_MPI=true) # test polynomials up to order ngrid-1 for n ∈ 0:ngrid-1 @@ -884,6 +902,8 @@ function runtests() "gausslegendre_pseudospectral", fd_option, cheb_option, bc, adv_input, comm, element_spacing_option) # create the coordinate struct 'x' and info for derivatives, etc. + # This test runs effectively in serial, so use `ignore_MPI=true` to avoid + # errors due to communicators not being fully set up. x, spectral = define_coordinate(input; ignore_MPI=true, init_YY=false) offset = randn(rng) @@ -1001,6 +1021,8 @@ function runtests() "gausslegendre_pseudospectral", fd_option, cheb_option, bc, adv_input, comm, element_spacing_option) # create the coordinate struct 'x' and info for derivatives, etc. + # This test runs effectively in serial, so use `ignore_MPI=true` to avoid + # errors due to communicators not being fully set up. x, spectral = define_coordinate(input; ignore_MPI=true, init_YY=false) offset = randn(rng) @@ -1045,6 +1067,8 @@ function runtests() "gausslegendre_pseudospectral", fd_option, cheb_option, bc, adv_input, comm, element_spacing_option) # create the coordinate struct 'x' and info for derivatives, etc. + # This test runs effectively in serial, so use `ignore_MPI=true` to avoid + # errors due to communicators not being fully set up. x, spectral = define_coordinate(input; ignore_MPI=true, init_YY=false) # test polynomials up to order ngrid-1 for n ∈ 0:ngrid-1 @@ -1096,6 +1120,8 @@ function runtests() "gausslegendre_pseudospectral", fd_option, cheb_option, bc, adv_input, comm, element_spacing_option) # create the coordinate struct 'x' and info for derivatives, etc. + # This test runs effectively in serial, so use `ignore_MPI=true` to avoid + # errors due to communicators not being fully set up. x, spectral = define_coordinate(input; ignore_MPI=true, init_YY=false) # test polynomials up to order ngrid-1 for n ∈ 0:ngrid-1 @@ -1310,6 +1336,8 @@ function runtests() "chebyshev_pseudospectral", fd_option, cheb_option, bc, adv_input, comm, element_spacing_option) # create the coordinate struct 'x' and info for derivatives, etc. + # This test runs effectively in serial, so use `ignore_MPI=true` to avoid + # errors due to communicators not being fully set up. x, spectral = define_coordinate(input; ignore_MPI=true) offset = randn(rng) @@ -1418,6 +1446,8 @@ function runtests() "gausslegendre_pseudospectral", fd_option, cheb_option, bc, adv_input, comm, element_spacing_option) # create the coordinate struct 'x' and info for derivatives, etc. + # This test runs effectively in serial, so use `ignore_MPI=true` to avoid + # errors due to communicators not being fully set up. x, spectral = define_coordinate(input; ignore_MPI=true, init_YY=false) offset = randn(rng) diff --git a/moment_kinetics/test/fokker_planck_time_evolution_tests.jl b/moment_kinetics/test/fokker_planck_time_evolution_tests.jl index 2d251082b..3039b934d 100644 --- a/moment_kinetics/test/fokker_planck_time_evolution_tests.jl +++ b/moment_kinetics/test/fokker_planck_time_evolution_tests.jl @@ -8,7 +8,7 @@ using moment_kinetics.coordinates: define_coordinate using moment_kinetics.input_structs: grid_input, advection_input using moment_kinetics.load_data: open_readonly_output_file, load_coordinate_data, load_species_data, load_fields_data, - load_charged_particle_moments_data, load_pdf_data, + load_ion_moments_data, load_pdf_data, load_time_data, load_species_data using moment_kinetics.type_definitions: mk_float @@ -24,14 +24,14 @@ struct expected_data vpa::Array{mk_float, 1} vperp::Array{mk_float, 1} phi::Array{mk_float, 1} #time - n_charged::Array{mk_float, 1} #time - upar_charged::Array{mk_float, 1} # time - ppar_charged::Array{mk_float, 1} # time - pperp_charged::Array{mk_float, 1} # time - qpar_charged::Array{mk_float, 1} # time - v_t_charged::Array{mk_float, 1} # time + n_ion::Array{mk_float, 1} #time + upar_ion::Array{mk_float, 1} # time + ppar_ion::Array{mk_float, 1} # time + pperp_ion::Array{mk_float, 1} # time + qpar_ion::Array{mk_float, 1} # time + v_t_ion::Array{mk_float, 1} # time dSdt::Array{mk_float, 1} # time - f_charged::Array{mk_float, 3} # vpa, vperp, time + f_ion::Array{mk_float, 3} # vpa, vperp, time end const expected = @@ -40,21 +40,21 @@ const expected = [0.155051025721682, 0.644948974278318, 1.000000000000000, 1.500000000000000, 2.000000000000000, 2.500000000000000, 3.000000000000000], # Expected phi: [-1.267505494648937, -1.275683298550937], - # Expected n_charged: + # Expected n_ion: [0.2815330322340072, 0.2792400986636072], - # Expected upar_charged: + # Expected upar_ion: [0.0, 0.0], - # Expected ppar_charged: + # Expected ppar_ion: [0.17982280248048935, 0.14891126175332367], - # Expected pperp_charged + # Expected pperp_ion [0.14340146667506784, 0.1581377822859991], - # Expected qpar_charged + # Expected qpar_ion [0.0, 0.0], - # Expected v_t_charged + # Expected v_t_ion [1.0511726083010418, 1.0538509291794658], # Expected dSdt [0.0, 1.1853081348031516e-5], - # Expected f_charged: + # Expected f_ion: [0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0006199600161806666 0.00047805300997075977 0.0002665817112117718 7.637693901737056e-5 1.3272321881722645e-5 1.3988924344690309e-6 0.0; 0.005882016862626724 0.0045356406743786385 0.002529256854781707 0.0007246442213864763 0.00012592428394890537 1.3272321881722645e-5 0.0; @@ -87,25 +87,25 @@ const expected = ########################################################################################## """ fid = open_readonly_output_file(path, "dfns") -f_charged_vpavperpzrst = load_pdf_data(fid) -f_charged = f_charged_vpavperpzrst[:,:,1,1,1,:] +f_ion_vpavperpzrst = load_pdf_data(fid) +f_ion = f_ion_vpavperpzrst[:,:,1,1,1,:] ntind = 2 nvpa = 13 #subject to grid choices nvperp = 7 #subject to grid choices for k in 1:ntind for j in 1:nvperp-1 for i in 1:nvpa-1 - @printf("%.15f ", f_charged[i,j,k]) + @printf("%.15f ", f_ion[i,j,k]) print("; ") end - @printf("%.15f ", f_charged[nvpa,j,k]) + @printf("%.15f ", f_ion[nvpa,j,k]) print(";;\n") end for i in 1:nvpa-1 - @printf("%.15f ", f_charged[i,nvperp,k]) + @printf("%.15f ", f_ion[i,nvperp,k]) print("; ") end - @printf("%.15f ", f_charged[nvpa,nvperp,k]) + @printf("%.15f ", f_ion[nvpa,nvperp,k]) if k < ntind print(";;;\n") end @@ -179,6 +179,10 @@ function run_test(test_input, rtol, atol, upar_rtol=nothing; args...) # by passing keyword arguments to run_test, args becomes a Dict which can be used to # update the default inputs + # Make a copy to make sure nothing modifies the input Dicts defined in this test + # script. + test_input = deepcopy(test_input) + if upar_rtol === nothing upar_rtol = rtol end @@ -211,14 +215,14 @@ function run_test(test_input, rtol, atol, upar_rtol=nothing; args...) end phi = nothing - n_charged = nothing - upar_charged = nothing - ppar_charged = nothing - pperp_charged = nothing - qpar_charged = nothing - v_t_charged = nothing + n_ion = nothing + upar_ion = nothing + ppar_ion = nothing + pperp_ion = nothing + qpar_ion = nothing + v_t_ion = nothing dSdt = nothing - f_charged = nothing + f_ion = nothing f_err = nothing vpa, vpa_spectral = nothing, nothing vperp, vperp_spectral = nothing, nothing @@ -243,8 +247,8 @@ function run_test(test_input, rtol, atol, upar_rtol=nothing; args...) phi_zrt, Er_zrt, Ez_zrt = load_fields_data(fid) # load velocity moments data - n_charged_zrst, upar_charged_zrst, ppar_charged_zrst, - pperp_charged_zrst, qpar_charged_zrst, v_t_charged_zrst, dSdt_zrst = load_charged_particle_moments_data(fid,extended_moments=true) + n_ion_zrst, upar_ion_zrst, ppar_ion_zrst, + pperp_ion_zrst, qpar_ion_zrst, v_t_ion_zrst, dSdt_zrst = load_ion_moments_data(fid,extended_moments=true) close(fid) @@ -255,21 +259,21 @@ function run_test(test_input, rtol, atol, upar_rtol=nothing; args...) vperp, vperp_spectral = load_coordinate_data(fid, "vperp") # load particle distribution function (pdf) data - f_charged_vpavperpzrst = load_pdf_data(fid) + f_ion_vpavperpzrst = load_pdf_data(fid) close(fid) # select the single z, r, s point # keep the two time points in the arrays phi = phi_zrt[1,1,:] - n_charged = n_charged_zrst[1,1,1,:] - upar_charged = upar_charged_zrst[1,1,1,:] - ppar_charged = ppar_charged_zrst[1,1,1,:] - pperp_charged = pperp_charged_zrst[1,1,1,:] - qpar_charged = qpar_charged_zrst[1,1,1,:] - v_t_charged = v_t_charged_zrst[1,1,1,:] + n_ion = n_ion_zrst[1,1,1,:] + upar_ion = upar_ion_zrst[1,1,1,:] + ppar_ion = ppar_ion_zrst[1,1,1,:] + pperp_ion = pperp_ion_zrst[1,1,1,:] + qpar_ion = qpar_ion_zrst[1,1,1,:] + v_t_ion = v_t_ion_zrst[1,1,1,:] dSdt = dSdt_zrst[1,1,1,:] - f_charged = f_charged_vpavperpzrst[:,:,1,1,1,:] - f_err = copy(f_charged) + f_ion = f_ion_vpavperpzrst[:,:,1,1,1,:] + f_err = copy(f_ion) # Unnormalize f # NEED TO UPGRADE TO 2V MOMENT KINETICS HERE @@ -288,20 +292,20 @@ function run_test(test_input, rtol, atol, upar_rtol=nothing; args...) @test isapprox(expected.phi[tind], phi[tind], rtol=rtol) - # Check charged particle moments and f + # Check ion particle moments and f ###################################### - @test isapprox(expected.n_charged[tind], n_charged[tind], atol=atol) - @test isapprox(expected.upar_charged[tind], upar_charged[tind], atol=atol) - @test isapprox(expected.ppar_charged[tind], ppar_charged[tind], atol=atol) - @test isapprox(expected.pperp_charged[tind], pperp_charged[tind], atol=atol) - @test isapprox(expected.qpar_charged[tind], qpar_charged[tind], atol=atol) - @test isapprox(expected.v_t_charged[tind], v_t_charged[tind], atol=atol) + @test isapprox(expected.n_ion[tind], n_ion[tind], atol=atol) + @test isapprox(expected.upar_ion[tind], upar_ion[tind], atol=atol) + @test isapprox(expected.ppar_ion[tind], ppar_ion[tind], atol=atol) + @test isapprox(expected.pperp_ion[tind], pperp_ion[tind], atol=atol) + @test isapprox(expected.qpar_ion[tind], qpar_ion[tind], atol=atol) + @test isapprox(expected.v_t_ion[tind], v_t_ion[tind], atol=atol) @test isapprox(expected.dSdt[tind], dSdt[tind], atol=atol) - @. f_err = abs(expected.f_charged - f_charged) + @. f_err = abs(expected.f_ion - f_ion) max_f_err = maximum(f_err) @test isapprox(max_f_err, 0.0, atol=atol) - @test isapprox(expected.f_charged[:,:,tind], f_charged[:,:,tind], atol=atol) + @test isapprox(expected.f_ion[:,:,tind], f_ion[:,:,tind], atol=atol) end end diff --git a/moment_kinetics/test/harrisonthompson.jl b/moment_kinetics/test/harrisonthompson.jl index 8ad05dd24..553515a19 100644 --- a/moment_kinetics/test/harrisonthompson.jl +++ b/moment_kinetics/test/harrisonthompson.jl @@ -139,7 +139,7 @@ test_input_chebyshev_split1 = merge(test_input_chebyshev, test_input_chebyshev_split2 = merge(test_input_chebyshev_split1, Dict("run_name" => "chebyshev_pseudospectral_split2", "evolve_moments_parallel_flow" => true, - "numerical_dissipation" => Dict("force_minimum_pdf_value" => 0.0))) + "ion_numerical_dissipation" => Dict("force_minimum_pdf_value" => 0.0))) test_input_chebyshev_split3 = merge(test_input_chebyshev_split2, Dict("run_name" => "chebyshev_pseudospectral_split3", @@ -154,6 +154,10 @@ function run_test(test_input, analytic_rtol, analytic_atol, expected_phi, # by passing keyword arguments to run_test, args becomes a Dict which can be used to # update the default inputs + # Make a copy to make sure nothing modifies the input Dicts defined in this test + # script. + test_input = deepcopy(test_input) + # Convert keyword arguments to a unique name name = test_input["run_name"] if length(args) > 0 diff --git a/moment_kinetics/test/interpolation_tests.jl b/moment_kinetics/test/interpolation_tests.jl index b2fd892a5..9b88deda7 100644 --- a/moment_kinetics/test/interpolation_tests.jl +++ b/moment_kinetics/test/interpolation_tests.jl @@ -44,6 +44,8 @@ function runtests() discretization, fd_option, cheb_option, bc, adv_input, comm, element_spacing_option) # create the coordinate struct 'z' + # This test runs effectively in serial, so use `ignore_MPI=true` to avoid + # errors due to communicators not being fully set up. z, spectral = define_coordinate(input; ignore_MPI=true) test_grid = [z for z in range(-zlim, zlim, length=ntest)] diff --git a/moment_kinetics/test/nonlinear_sound_wave_inputs_and_expected_data.jl b/moment_kinetics/test/nonlinear_sound_wave_inputs_and_expected_data.jl index e9d46b1fc..cdee05ac9 100644 --- a/moment_kinetics/test/nonlinear_sound_wave_inputs_and_expected_data.jl +++ b/moment_kinetics/test/nonlinear_sound_wave_inputs_and_expected_data.jl @@ -7,13 +7,13 @@ struct expected_data z::Array{mk_float, 1} vpa::Array{mk_float, 1} phi::Array{mk_float, 2} - n_charged::Array{mk_float, 2} + n_ion::Array{mk_float, 2} n_neutral::Array{mk_float, 2} - upar_charged::Array{mk_float, 2} + upar_ion::Array{mk_float, 2} upar_neutral::Array{mk_float, 2} - ppar_charged::Array{mk_float, 2} + ppar_ion::Array{mk_float, 2} ppar_neutral::Array{mk_float, 2} - f_charged::Array{mk_float, 3} + f_ion::Array{mk_float, 3} f_neutral::Array{mk_float, 3} end @@ -32,48 +32,48 @@ const expected = -0.353459763648872 -0.3755265897483555; -0.5494724768120175 -0.5903597644911376; -0.8609860698164502 -0.8726370464896476; -1.2115129555832849 -1.1306355658313922; -1.3862820803244258 -1.2382641646968997], - n_charged=[0.2500030702177186 0.2898869775083742; 0.2977473631375158 0.3228278662412625; - 0.42274585818529853 0.417848119539277; 0.5772542465450629 0.5541281150892785; - 0.7022542481909738 0.6869277664245242; 0.7499999999999394 0.7466605958319346; - 0.7022542481909738 0.6869277664245237; 0.577254246545063 0.5541281150892783; - 0.42274585818529864 0.41784811953927686; 0.2977473631375159 0.32282786624126253; - 0.2500030702177185 0.2898869775083743], + n_ion=[0.2500030702177186 0.2898869775083742; 0.2977473631375158 0.3228278662412625; + 0.42274585818529853 0.417848119539277; 0.5772542465450629 0.5541281150892785; + 0.7022542481909738 0.6869277664245242; 0.7499999999999394 0.7466605958319346; + 0.7022542481909738 0.6869277664245237; 0.577254246545063 0.5541281150892783; + 0.42274585818529864 0.41784811953927686; 0.2977473631375159 0.32282786624126253; + 0.2500030702177185 0.2898869775083743], n_neutral=[0.7499999999999382 0.7736769553678673; 0.7022542481909748 0.7056866352169496; 0.5772542465450632 0.5582977481633454; 0.4227458581852985 0.40969188756651037; 0.29774736313751604 0.30539644783353687; 0.2500030702177186 0.268198658560817; 0.29774736313751604 0.305396447833537; 0.42274585818529836 0.4096918875665103; 0.5772542465450631 0.5582977481633457; 0.7022542481909745 0.7056866352169494; 0.7499999999999383 0.7736769553678673], - upar_charged=[-2.7135787559953277e-17 -6.299214622140781e-17; -9.321028970172899e-18 -0.1823721921091055; - -2.8374879811351724e-18 -0.19657035490893093; 1.2124327390522635e-17 -0.11139486685283827; - 3.6525788403693063e-17 -0.033691837771623996; -2.0930856430671915e-17 4.84147091991613e-17; - 8.753545920086251e-18 0.033691837771624024; 1.1293771270243255e-17 0.11139486685283813; - 1.3739171132886587e-17 0.19657035490893102; -6.840453743089351e-18 0.18237219210910513; - -2.7135787559953277e-17 -4.656897959900552e-17], + upar_ion=[-2.7135787559953277e-17 -6.299214622140781e-17; -9.321028970172899e-18 -0.1823721921091055; + -2.8374879811351724e-18 -0.19657035490893093; 1.2124327390522635e-17 -0.11139486685283827; + 3.6525788403693063e-17 -0.033691837771623996; -2.0930856430671915e-17 4.84147091991613e-17; + 8.753545920086251e-18 0.033691837771624024; 1.1293771270243255e-17 0.11139486685283813; + 1.3739171132886587e-17 0.19657035490893102; -6.840453743089351e-18 0.18237219210910513; + -2.7135787559953277e-17 -4.656897959900552e-17], upar_neutral=[6.5569385065066925e-18 7.469475342067322e-17; 1.1054500872839027e-17 -0.036209130454625794; -3.241833393685864e-17 -0.00915544640981337; -3.617637280460899e-17 0.05452268209340691; 4.417578961284041e-17 0.07606644718003618; 4.9354467746194965e-17 4.452343983947504e-17; 6.573091229872379e-18 -0.07606644718003616; 2.989662686945165e-17 -0.05452268209340687; -3.1951996361666834e-17 0.009155446409813412; -4.395464518158184e-18 0.03620913045462582; 6.5569385065066925e-18 7.150569974151354e-17], - ppar_charged=[0.18749999999999992 0.2328164829490338; 0.20909325514551116 0.21912575009260987; - 0.24403180771238264 0.20822611102296495; 0.24403180771238278 0.21506741942934832; - 0.2090932551455113 0.22097085045011763; 0.1875 0.22119050467096843; - 0.20909325514551128 0.2209708504501176; 0.2440318077123828 0.2150674194293483; - 0.24403180771238256 0.20822611102296476; 0.20909325514551116 0.21912575009260982; - 0.18749999999999992 0.2328164829490338], + ppar_ion=[0.18749999999999992 0.2328164829490338; 0.20909325514551116 0.21912575009260987; + 0.24403180771238264 0.20822611102296495; 0.24403180771238278 0.21506741942934832; + 0.2090932551455113 0.22097085045011763; 0.1875 0.22119050467096843; + 0.20909325514551128 0.2209708504501176; 0.2440318077123828 0.2150674194293483; + 0.24403180771238256 0.20822611102296476; 0.20909325514551116 0.21912575009260982; + 0.18749999999999992 0.2328164829490338], ppar_neutral=[0.18750000000000003 0.2480244331470989; 0.20909325514551122 0.2440075646485762; 0.24403180771238286 0.22861256884534023; 0.24403180771238278 0.20588932618946498; 0.20909325514551144 0.19263633346696638; 0.18749999999999992 0.19091848744561835; 0.20909325514551141 0.19263633346696654; 0.2440318077123828 0.20588932618946482; 0.24403180771238286 0.22861256884534029; 0.20909325514551114 0.24400756464857642; 0.18750000000000006 0.24802443314709893], - f_charged=[0.0370462360994826 0.04059927063892091 0.0428431419871786 0.030398267195914062 0.01236045902698859 0.006338529470383425 0.012360459026988587 0.030398267195914028 0.04284314198717859 0.0405992706389209 0.0370462360994826; - 0.20411991941198782 0.25123395823993105 0.3934413727192304 0.6277900619432855 0.9100364506661008 1.0606601717796504 0.910036450666101 0.6277900619432859 0.39344137271923046 0.25123395823993094 0.20411991941198776; - 0.0370462360994826 0.04059927063892091 0.0428431419871786 0.030398267195914062 0.01236045902698859 0.006338529470383425 0.012360459026988587 0.030398267195914028 0.04284314198717859 0.0405992706389209 0.0370462360994826;;; - 0.05392403019146985 0.06057819609646438 0.03676744157455075 0.013740507879552622 0.010777319583092297 0.019330359159894384 0.027982173790396116 0.027603104735767332 0.02667986700464528 0.035654512254837005 0.05392403019146984; - 0.21177720235387912 0.24902901234066305 0.3729377138332225 0.596281539172339 0.8870867512643452 1.0533860567375264 0.887086751264345 0.5962815391723388 0.3729377138332225 0.24902901234066285 0.21177720235387912; - 0.053924030191469796 0.035654512254837074 0.02667986700464531 0.02760310473576733 0.02798217379039615 0.019330359159894287 0.010777319583092311 0.013740507879552624 0.03676744157455069 0.060578196096464365 0.05392403019146979], + f_ion=[0.0370462360994826 0.04059927063892091 0.0428431419871786 0.030398267195914062 0.01236045902698859 0.006338529470383425 0.012360459026988587 0.030398267195914028 0.04284314198717859 0.0405992706389209 0.0370462360994826; + 0.20411991941198782 0.25123395823993105 0.3934413727192304 0.6277900619432855 0.9100364506661008 1.0606601717796504 0.910036450666101 0.6277900619432859 0.39344137271923046 0.25123395823993094 0.20411991941198776; + 0.0370462360994826 0.04059927063892091 0.0428431419871786 0.030398267195914062 0.01236045902698859 0.006338529470383425 0.012360459026988587 0.030398267195914028 0.04284314198717859 0.0405992706389209 0.0370462360994826;;; + 0.05392403019146985 0.06057819609646438 0.03676744157455075 0.013740507879552622 0.010777319583092297 0.019330359159894384 0.027982173790396116 0.027603104735767332 0.02667986700464528 0.035654512254837005 0.05392403019146984; + 0.21177720235387912 0.24902901234066305 0.3729377138332225 0.596281539172339 0.8870867512643452 1.0533860567375264 0.887086751264345 0.5962815391723388 0.3729377138332225 0.24902901234066285 0.21177720235387912; + 0.053924030191469796 0.035654512254837074 0.02667986700464531 0.02760310473576733 0.02798217379039615 0.019330359159894287 0.010777319583092311 0.013740507879552624 0.03676744157455069 0.060578196096464365 0.05392403019146979], f_neutral=[0.0063385294703834595 0.012360459026988546 0.030398267195914108 0.04284314198717859 0.040599270638920985 0.03704623609948259 0.040599270638920965 0.0428431419871786 0.030398267195914094 0.012360459026988546 0.006338529470383456; 1.0606601717796493 0.9100364506661016 0.6277900619432857 0.3934413727192303 0.2512339582399308 0.20411991941198754 0.2512339582399307 0.3934413727192301 0.6277900619432853 0.9100364506661016 1.0606601717796487; 0.0063385294703834595 0.012360459026988546 0.030398267195914108 0.04284314198717859 0.040599270638920985 0.03704623609948259 0.040599270638920965 0.0428431419871786 0.030398267195914094 0.012360459026988546 0.006338529470383456;;; diff --git a/moment_kinetics/test/nonlinear_sound_wave_tests.jl b/moment_kinetics/test/nonlinear_sound_wave_tests.jl index 7a1e5dde5..0bb965494 100644 --- a/moment_kinetics/test/nonlinear_sound_wave_tests.jl +++ b/moment_kinetics/test/nonlinear_sound_wave_tests.jl @@ -8,7 +8,7 @@ using moment_kinetics.coordinates: define_coordinate using moment_kinetics.input_structs: grid_input, advection_input using moment_kinetics.load_data: open_readonly_output_file, load_coordinate_data, load_species_data, load_fields_data, - load_charged_particle_moments_data, load_pdf_data, + load_ion_moments_data, load_pdf_data, load_neutral_particle_moments_data, load_neutral_pdf_data, load_time_data, load_species_data using moment_kinetics.interpolation: interpolate_to_grid_z, interpolate_to_grid_vpa @@ -27,6 +27,10 @@ function run_test(test_input, rtol, atol, upar_rtol=nothing; args...) # by passing keyword arguments to run_test, args becomes a Dict which can be used to # update the default inputs + # Make a copy to make sure nothing modifies the input Dicts defined in this test + # script. + test_input = deepcopy(test_input) + if upar_rtol === nothing upar_rtol = rtol end @@ -58,10 +62,10 @@ function run_test(test_input, rtol, atol, upar_rtol=nothing; args...) end phi = nothing - n_charged = nothing - upar_charged = nothing - ppar_charged = nothing - f_charged = nothing + n_ion = nothing + upar_ion = nothing + ppar_ion = nothing + f_ion = nothing n_neutral = nothing upar_neutral = nothing ppar_neutral = nothing @@ -89,7 +93,7 @@ function run_test(test_input, rtol, atol, upar_rtol=nothing; args...) phi_zrt, Er_zrt, Ez_zrt = load_fields_data(fid) # load velocity moments data - n_charged_zrst, upar_charged_zrst, ppar_charged_zrst, qpar_charged_zrst, v_t_charged_zrst = load_charged_particle_moments_data(fid) + n_ion_zrst, upar_ion_zrst, ppar_ion_zrst, qpar_ion_zrst, v_t_ion_zrst = load_ion_moments_data(fid) n_neutral_zrst, upar_neutral_zrst, ppar_neutral_zrst, qpar_neutral_zrst, v_t_neutral_zrst = load_neutral_particle_moments_data(fid) z, z_spectral = load_coordinate_data(fid, "z") @@ -99,19 +103,19 @@ function run_test(test_input, rtol, atol, upar_rtol=nothing; args...) fid = open_readonly_output_file(path, "dfns") # load particle distribution function (pdf) data - f_charged_vpavperpzrst = load_pdf_data(fid) + f_ion_vpavperpzrst = load_pdf_data(fid) f_neutral_vzvrvzetazrst = load_neutral_pdf_data(fid) vpa, vpa_spectral = load_coordinate_data(fid, "vpa") close(fid) phi = phi_zrt[:,1,:] - n_charged = n_charged_zrst[:,1,:,:] - upar_charged = upar_charged_zrst[:,1,:,:] - ppar_charged = ppar_charged_zrst[:,1,:,:] - qpar_charged = qpar_charged_zrst[:,1,:,:] - v_t_charged = v_t_charged_zrst[:,1,:,:] - f_charged = f_charged_vpavperpzrst[:,1,:,1,:,:] + n_ion = n_ion_zrst[:,1,:,:] + upar_ion = upar_ion_zrst[:,1,:,:] + ppar_ion = ppar_ion_zrst[:,1,:,:] + qpar_ion = qpar_ion_zrst[:,1,:,:] + v_t_ion = v_t_ion_zrst[:,1,:,:] + f_ion = f_ion_vpavperpzrst[:,1,:,1,:,:] n_neutral = n_neutral_zrst[:,1,:,:] upar_neutral = upar_neutral_zrst[:,1,:,:] ppar_neutral = ppar_neutral_zrst[:,1,:,:] @@ -122,7 +126,7 @@ function run_test(test_input, rtol, atol, upar_rtol=nothing; args...) # Unnormalize f if input["evolve_moments_density"] for it ∈ 1:length(time), is ∈ 1:n_ion_species, iz ∈ 1:z.n - f_charged[:,iz,is,it] .*= n_charged[iz,is,it] + f_ion[:,iz,is,it] .*= n_ion[iz,is,it] end for it ∈ 1:length(time), isn ∈ 1:n_neutral_species, iz ∈ 1:z.n f_neutral[:,iz,isn,it] .*= n_neutral[iz,isn,it] @@ -130,7 +134,7 @@ function run_test(test_input, rtol, atol, upar_rtol=nothing; args...) end if input["evolve_moments_parallel_pressure"] for it ∈ 1:length(time), is ∈ 1:n_ion_species, iz ∈ 1:z.n - f_charged[:,iz,is,it] ./= v_t_charged[iz,is,it] + f_ion[:,iz,is,it] ./= v_t_ion[iz,is,it] end for it ∈ 1:length(time), isn ∈ 1:n_neutral_species, iz ∈ 1:z.n f_neutral[:,iz,isn,it] ./= v_t_neutral[iz,isn,it] @@ -148,11 +152,11 @@ function run_test(test_input, rtol, atol, upar_rtol=nothing; args...) #println("phi ", size(newgrid_phi)) #println(newgrid_phi) #println() - #newgrid_n_charged = cat(interpolate_to_grid_z(expected.z, n_charged[:, :, 1], z, z_spectral)[:,1], - # interpolate_to_grid_z(expected.z, n_charged[:, :, 2], z, z_spectral)[:,1]; + #newgrid_n_ion = cat(interpolate_to_grid_z(expected.z, n_ion[:, :, 1], z, z_spectral)[:,1], + # interpolate_to_grid_z(expected.z, n_ion[:, :, 2], z, z_spectral)[:,1]; # dims=2) - #println("n_charged ", size(newgrid_n_charged)) - #println(newgrid_n_charged) + #println("n_ion ", size(newgrid_n_ion)) + #println(newgrid_n_ion) #println() #newgrid_n_neutral = cat(interpolate_to_grid_z(expected.z, n_neutral[:, :, 1], z, z_spectral)[:,1], # interpolate_to_grid_z(expected.z, n_neutral[:, :, 2], z, z_spectral)[:,1]; @@ -160,11 +164,11 @@ function run_test(test_input, rtol, atol, upar_rtol=nothing; args...) #println("n_neutral ", size(newgrid_n_neutral)) #println(newgrid_n_neutral) #println() - #newgrid_upar_charged = cat(interpolate_to_grid_z(expected.z, upar_charged[:, :, 1], z, z_spectral)[:,1], - # interpolate_to_grid_z(expected.z, upar_charged[:, :, 2], z, z_spectral)[:,1]; + #newgrid_upar_ion = cat(interpolate_to_grid_z(expected.z, upar_ion[:, :, 1], z, z_spectral)[:,1], + # interpolate_to_grid_z(expected.z, upar_ion[:, :, 2], z, z_spectral)[:,1]; # dims=2) - #println("upar_charged ", size(newgrid_upar_charged)) - #println(newgrid_upar_charged) + #println("upar_ion ", size(newgrid_upar_ion)) + #println(newgrid_upar_ion) #println() #newgrid_upar_neutral = cat(interpolate_to_grid_z(expected.z, upar_neutral[:, :, 1], z, z_spectral)[:,1], # interpolate_to_grid_z(expected.z, upar_neutral[:, :, 2], z, z_spectral)[:,1]; @@ -172,11 +176,11 @@ function run_test(test_input, rtol, atol, upar_rtol=nothing; args...) #println("upar_neutral ", size(newgrid_upar_neutral)) #println(newgrid_upar_neutral) #println() - #newgrid_ppar_charged = cat(interpolate_to_grid_z(expected.z, ppar_charged[:, :, 1], z, z_spectral)[:,1], - # interpolate_to_grid_z(expected.z, ppar_charged[:, :, 2], z, z_spectral)[:,1]; + #newgrid_ppar_ion = cat(interpolate_to_grid_z(expected.z, ppar_ion[:, :, 1], z, z_spectral)[:,1], + # interpolate_to_grid_z(expected.z, ppar_ion[:, :, 2], z, z_spectral)[:,1]; # dims=2) - #println("ppar_charged ", size(newgrid_ppar_charged)) - #println(newgrid_ppar_charged) + #println("ppar_ion ", size(newgrid_ppar_ion)) + #println(newgrid_ppar_ion) #println() #newgrid_ppar_neutral = cat(interpolate_to_grid_z(expected.z, ppar_neutral[:, :, 1], z, z_spectral)[:,1], # interpolate_to_grid_z(expected.z, ppar_neutral[:, :, 2], z, z_spectral)[:,1]; @@ -184,11 +188,11 @@ function run_test(test_input, rtol, atol, upar_rtol=nothing; args...) #println("ppar_neutral ", size(newgrid_ppar_neutral)) #println(newgrid_ppar_neutral) #println() - #newgrid_f_charged = cat(interpolate_to_grid_vpa(expected.vpa, interpolate_to_grid_z(expected.z, f_charged[:, :, :, 1], z, z_spectral), vpa, vpa_spectral)[:,:,1], - # interpolate_to_grid_vpa(expected.vpa, interpolate_to_grid_z(expected.z, f_charged[:, :, :, 2], z, z_spectral), vpa, vpa_spectral)[:,:,1]; + #newgrid_f_ion = cat(interpolate_to_grid_vpa(expected.vpa, interpolate_to_grid_z(expected.z, f_ion[:, :, :, 1], z, z_spectral), vpa, vpa_spectral)[:,:,1], + # interpolate_to_grid_vpa(expected.vpa, interpolate_to_grid_z(expected.z, f_ion[:, :, :, 2], z, z_spectral), vpa, vpa_spectral)[:,:,1]; # dims=4) - #println("f_charged ", size(newgrid_f_charged)) - #println(newgrid_f_charged) + #println("f_ion ", size(newgrid_f_ion)) + #println(newgrid_f_ion) #println() #newgrid_f_neutral = cat(interpolate_to_grid_vpa(expected.vpa, interpolate_to_grid_z(expected.z, f_neutral[:, :, :, 1], z, z_spectral), vpa, vpa_spectral)[:,:,1], # interpolate_to_grid_vpa(expected.vpa, interpolate_to_grid_z(expected.z, f_neutral[:, :, :, 2], z, z_spectral), vpa, vpa_spectral)[:,:,1]; @@ -201,36 +205,36 @@ function run_test(test_input, rtol, atol, upar_rtol=nothing; args...) newgrid_phi = interpolate_to_grid_z(expected.z, phi[:, tind], z, z_spectral) @test isapprox(expected.phi[:, tind], newgrid_phi, rtol=rtol) - # Check charged particle moments and f + # Check ion particle moments and f ###################################### - newgrid_n_charged = interpolate_to_grid_z(expected.z, n_charged[:, :, tind], z, z_spectral) - @test isapprox(expected.n_charged[:, tind], newgrid_n_charged[:,1], rtol=rtol) + newgrid_n_ion = interpolate_to_grid_z(expected.z, n_ion[:, :, tind], z, z_spectral) + @test isapprox(expected.n_ion[:, tind], newgrid_n_ion[:,1], rtol=rtol) - newgrid_upar_charged = interpolate_to_grid_z(expected.z, upar_charged[:, :, tind], z, z_spectral) - @test isapprox(expected.upar_charged[:, tind], newgrid_upar_charged[:,1], rtol=upar_rtol, atol=atol) + newgrid_upar_ion = interpolate_to_grid_z(expected.z, upar_ion[:, :, tind], z, z_spectral) + @test isapprox(expected.upar_ion[:, tind], newgrid_upar_ion[:,1], rtol=upar_rtol, atol=atol) - newgrid_ppar_charged = interpolate_to_grid_z(expected.z, ppar_charged[:, :, tind], z, z_spectral) - @test isapprox(expected.ppar_charged[:, tind], newgrid_ppar_charged[:,1], rtol=rtol) + newgrid_ppar_ion = interpolate_to_grid_z(expected.z, ppar_ion[:, :, tind], z, z_spectral) + @test isapprox(expected.ppar_ion[:, tind], newgrid_ppar_ion[:,1], rtol=rtol) - newgrid_vth_charged = @. sqrt(2.0*newgrid_ppar_charged/newgrid_n_charged) - newgrid_f_charged = interpolate_to_grid_z(expected.z, f_charged[:, :, :, tind], z, z_spectral) - temp = newgrid_f_charged - newgrid_f_charged = fill(NaN, length(expected.vpa), - size(newgrid_f_charged, 2), - size(newgrid_f_charged, 3), - size(newgrid_f_charged, 4)) + newgrid_vth_ion = @. sqrt(2.0*newgrid_ppar_ion/newgrid_n_ion) + newgrid_f_ion = interpolate_to_grid_z(expected.z, f_ion[:, :, :, tind], z, z_spectral) + temp = newgrid_f_ion + newgrid_f_ion = fill(NaN, length(expected.vpa), + size(newgrid_f_ion, 2), + size(newgrid_f_ion, 3), + size(newgrid_f_ion, 4)) for iz ∈ 1:length(expected.z) wpa = copy(expected.vpa) if input["evolve_moments_parallel_flow"] - wpa .-= newgrid_upar_charged[iz,1] + wpa .-= newgrid_upar_ion[iz,1] end if input["evolve_moments_parallel_pressure"] - wpa ./= newgrid_vth_charged[iz,1] + wpa ./= newgrid_vth_ion[iz,1] end - newgrid_f_charged[:,iz,1] = interpolate_to_grid_vpa(wpa, temp[:,iz,1], vpa, vpa_spectral) + newgrid_f_ion[:,iz,1] = interpolate_to_grid_vpa(wpa, temp[:,iz,1], vpa, vpa_spectral) end - @test isapprox(expected.f_charged[:, :, tind], newgrid_f_charged[:,:,1], rtol=rtol) + @test isapprox(expected.f_ion[:, :, tind], newgrid_f_ion[:,:,1], rtol=rtol) # Check neutral particle moments and f ###################################### diff --git a/moment_kinetics/test/recycling_fraction_tests.jl b/moment_kinetics/test/recycling_fraction_tests.jl index d4b833817..9e3c1697d 100644 --- a/moment_kinetics/test/recycling_fraction_tests.jl +++ b/moment_kinetics/test/recycling_fraction_tests.jl @@ -105,7 +105,8 @@ test_input_split3 = merge(test_input_split2, "vpa_nelement" => 31, "vz_nelement" => 31, "evolve_moments_parallel_pressure" => true, - "numerical_dissipation" => Dict{String,Any}("force_minimum_pdf_value" => 0.0, "vpa_dissipation_coefficient" => 1e-2))) + "ion_numerical_dissipation" => Dict{String,Any}("force_minimum_pdf_value" => 0.0, "vpa_dissipation_coefficient" => 1e-2), + "neutral_numerical_dissipation" => Dict{String,Any}("force_minimum_pdf_value" => 0.0, "vz_dissipation_coefficient" => 1e-2))) test_input_split3["timestepping"] = merge(test_input_split3["timestepping"], Dict("dt" => 1.0e-5)) @@ -166,6 +167,10 @@ function run_test(test_input, expected_phi; rtol=4.e-14, atol=1.e-15, args...) # by passing keyword arguments to run_test, args becomes a Tuple of Pairs which can be # used to update the default inputs + # Make a copy to make sure nothing modifies the input Dicts defined in this test + # script. + test_input = deepcopy(test_input) + # Convert keyword arguments to a unique name name = test_input["run_name"] if length(args) > 0 diff --git a/moment_kinetics/test/restart_interpolation_tests.jl b/moment_kinetics/test/restart_interpolation_tests.jl index b8c8899c3..9c0a1c99b 100644 --- a/moment_kinetics/test/restart_interpolation_tests.jl +++ b/moment_kinetics/test/restart_interpolation_tests.jl @@ -12,7 +12,7 @@ using moment_kinetics.file_io: io_has_parallel using moment_kinetics.input_structs: grid_input, advection_input, hdf5 using moment_kinetics.load_data: open_readonly_output_file, load_coordinate_data, load_species_data, load_fields_data, - load_charged_particle_moments_data, load_pdf_data, + load_ion_moments_data, load_pdf_data, load_neutral_particle_moments_data, load_neutral_pdf_data, load_time_data, load_species_data using moment_kinetics.interpolation: interpolate_to_grid_z, interpolate_to_grid_vpa @@ -70,6 +70,10 @@ function run_test(test_input, base, message, rtol, atol; tol_3V, kwargs...) # by passing keyword arguments to run_test, kwargs becomes a Tuple of Pairs which can be used to # update the default inputs + # Make a copy to make sure nothing modifies the input Dicts defined in this test + # script. + test_input = deepcopy(test_input) + if tol_3V === nothing atol_3V = atol rtol_3V = rtol @@ -92,10 +96,15 @@ function run_test(test_input, base, message, rtol, atol; tol_3V, kwargs...) println(" - testing ", message) # Convert from Tuple of Pairs with symbol keys to Dict with String keys - modified_inputs = Dict(String(k) => v for (k, v) in kwargs) + modified_inputs = Dict(String(k) => v for (k, v) in kwargs + if String(k) ∉ keys(test_input["timestepping"])) + modified_timestepping_inputs = Dict(String(k) => v for (k, v) in kwargs + if String(k) ∈ keys(test_input["timestepping"])) # Update default inputs with values to be changed input = merge(test_input, modified_inputs) + input["timestepping"] = merge(test_input["timestepping"], + modified_timestepping_inputs) input["run_name"] = name @@ -115,10 +124,10 @@ function run_test(test_input, base, message, rtol, atol; tol_3V, kwargs...) end phi = nothing - n_charged = nothing - upar_charged = nothing - ppar_charged = nothing - f_charged = nothing + n_ion = nothing + upar_ion = nothing + ppar_ion = nothing + f_ion = nothing n_neutral = nothing upar_neutral = nothing ppar_neutral = nothing @@ -150,12 +159,12 @@ function run_test(test_input, base, message, rtol, atol; tol_3V, kwargs...) time = run_info.time n_ion_species = run_info.n_ion_species n_neutral_species = run_info.n_neutral_species - n_charged_zrst = postproc_load_variable(run_info, "density") - upar_charged_zrst = postproc_load_variable(run_info, "parallel_flow") - ppar_charged_zrst = postproc_load_variable(run_info, "parallel_pressure") - qpar_charged_zrst = postproc_load_variable(run_info, "parallel_heat_flux") - v_t_charged_zrst = postproc_load_variable(run_info, "thermal_speed") - f_charged_vpavperpzrst = postproc_load_variable(run_info, "f") + n_ion_zrst = postproc_load_variable(run_info, "density") + upar_ion_zrst = postproc_load_variable(run_info, "parallel_flow") + ppar_ion_zrst = postproc_load_variable(run_info, "parallel_pressure") + qpar_ion_zrst = postproc_load_variable(run_info, "parallel_heat_flux") + v_t_ion_zrst = postproc_load_variable(run_info, "thermal_speed") + f_ion_vpavperpzrst = postproc_load_variable(run_info, "f") n_neutral_zrst = postproc_load_variable(run_info, "density_neutral") upar_neutral_zrst = postproc_load_variable(run_info, "uz_neutral") ppar_neutral_zrst = postproc_load_variable(run_info, "pz_neutral") @@ -176,12 +185,12 @@ function run_test(test_input, base, message, rtol, atol; tol_3V, kwargs...) rm(joinpath(realpath(input["base_directory"]), name); recursive=true) phi = phi_zrt[:,1,:] - n_charged = n_charged_zrst[:,1,:,:] - upar_charged = upar_charged_zrst[:,1,:,:] - ppar_charged = ppar_charged_zrst[:,1,:,:] - qpar_charged = qpar_charged_zrst[:,1,:,:] - v_t_charged = v_t_charged_zrst[:,1,:,:] - f_charged = f_charged_vpavperpzrst[:,1,:,1,:,:] + n_ion = n_ion_zrst[:,1,:,:] + upar_ion = upar_ion_zrst[:,1,:,:] + ppar_ion = ppar_ion_zrst[:,1,:,:] + qpar_ion = qpar_ion_zrst[:,1,:,:] + v_t_ion = v_t_ion_zrst[:,1,:,:] + f_ion = f_ion_vpavperpzrst[:,1,:,1,:,:] n_neutral = n_neutral_zrst[:,1,:,:] upar_neutral = upar_neutral_zrst[:,1,:,:] ppar_neutral = ppar_neutral_zrst[:,1,:,:] @@ -192,7 +201,7 @@ function run_test(test_input, base, message, rtol, atol; tol_3V, kwargs...) # Unnormalize f if input["evolve_moments_density"] for it ∈ 1:length(time), is ∈ 1:n_ion_species, iz ∈ 1:z.n - f_charged[:,iz,is,it] .*= n_charged[iz,is,it] + f_ion[:,iz,is,it] .*= n_ion[iz,is,it] end for it ∈ 1:length(time), isn ∈ 1:n_neutral_species, iz ∈ 1:z.n f_neutral[:,iz,isn,it] .*= n_neutral[iz,isn,it] @@ -200,7 +209,7 @@ function run_test(test_input, base, message, rtol, atol; tol_3V, kwargs...) end if input["evolve_moments_parallel_pressure"] for it ∈ 1:length(time), is ∈ 1:n_ion_species, iz ∈ 1:z.n - f_charged[:,iz,is,it] ./= v_t_charged[iz,is,it] + f_ion[:,iz,is,it] ./= v_t_ion[iz,is,it] end for it ∈ 1:length(time), isn ∈ 1:n_neutral_species, iz ∈ 1:z.n f_neutral[:,iz,isn,it] ./= v_t_neutral[iz,isn,it] @@ -211,36 +220,36 @@ function run_test(test_input, base, message, rtol, atol; tol_3V, kwargs...) newgrid_phi = interpolate_to_grid_z(expected.z, phi[:, end], z, z_spectral) @test isapprox(expected.phi[:, end], newgrid_phi, rtol=rtol) - # Check charged particle moments and f + # Check ion particle moments and f ###################################### - newgrid_n_charged = interpolate_to_grid_z(expected.z, n_charged[:, :, end], z, z_spectral) - @test isapprox(expected.n_charged[:, end], newgrid_n_charged[:,1], rtol=rtol) + newgrid_n_ion = interpolate_to_grid_z(expected.z, n_ion[:, :, end], z, z_spectral) + @test isapprox(expected.n_ion[:, end], newgrid_n_ion[:,1], rtol=rtol) - newgrid_upar_charged = interpolate_to_grid_z(expected.z, upar_charged[:, :, end], z, z_spectral) - @test isapprox(expected.upar_charged[:, end], newgrid_upar_charged[:,1], rtol=rtol, atol=atol_3V) + newgrid_upar_ion = interpolate_to_grid_z(expected.z, upar_ion[:, :, end], z, z_spectral) + @test isapprox(expected.upar_ion[:, end], newgrid_upar_ion[:,1], rtol=rtol, atol=atol_3V) - newgrid_ppar_charged = interpolate_to_grid_z(expected.z, ppar_charged[:, :, end], z, z_spectral) - @test isapprox(expected.ppar_charged[:, end], newgrid_ppar_charged[:,1], rtol=rtol) + newgrid_ppar_ion = interpolate_to_grid_z(expected.z, ppar_ion[:, :, end], z, z_spectral) + @test isapprox(expected.ppar_ion[:, end], newgrid_ppar_ion[:,1], rtol=rtol) - newgrid_vth_charged = @. sqrt(2.0*newgrid_ppar_charged/newgrid_n_charged) - newgrid_f_charged = interpolate_to_grid_z(expected.z, f_charged[:, :, :, end], z, z_spectral) - temp = newgrid_f_charged - newgrid_f_charged = fill(NaN, length(expected.vpa), - size(newgrid_f_charged, 2), - size(newgrid_f_charged, 3), - size(newgrid_f_charged, 4)) + newgrid_vth_ion = @. sqrt(2.0*newgrid_ppar_ion/newgrid_n_ion) + newgrid_f_ion = interpolate_to_grid_z(expected.z, f_ion[:, :, :, end], z, z_spectral) + temp = newgrid_f_ion + newgrid_f_ion = fill(NaN, length(expected.vpa), + size(newgrid_f_ion, 2), + size(newgrid_f_ion, 3), + size(newgrid_f_ion, 4)) for iz ∈ 1:length(expected.z) wpa = copy(expected.vpa) if input["evolve_moments_parallel_flow"] - wpa .-= newgrid_upar_charged[iz,1] + wpa .-= newgrid_upar_ion[iz,1] end if input["evolve_moments_parallel_pressure"] - wpa ./= newgrid_vth_charged[iz,1] + wpa ./= newgrid_vth_ion[iz,1] end - newgrid_f_charged[:,iz,1] = interpolate_to_grid_vpa(wpa, temp[:,iz,1], vpa, vpa_spectral) + newgrid_f_ion[:,iz,1] = interpolate_to_grid_vpa(wpa, temp[:,iz,1], vpa, vpa_spectral) end - @test isapprox(expected.f_charged[:, :, end], newgrid_f_charged[:,:,1], rtol=rtol_3V) + @test isapprox(expected.f_ion[:, :, end], newgrid_f_ion[:,:,1], rtol=rtol_3V) # Check neutral particle moments and f ###################################### @@ -299,7 +308,7 @@ function runtests() Dict("evolve_moments_parallel_pressure" => true, "vpa_L" => 1.5*vpa_L, "vz_L" => 1.5*vpa_L)) - for (base, base_label) ∈ ((base_input, "full-f"), + for (base, base_label) ∈ ((base_input_full_f, "full-f"), (base_input_evolve_density, "split 1"), (base_input_evolve_upar, "split 2"), (base_input_evolve_ppar, "split 3")) diff --git a/moment_kinetics/test/sound_wave_tests.jl b/moment_kinetics/test/sound_wave_tests.jl index ddfc4d078..813ed5a99 100644 --- a/moment_kinetics/test/sound_wave_tests.jl +++ b/moment_kinetics/test/sound_wave_tests.jl @@ -136,6 +136,10 @@ function run_test(test_input, analytic_frequency, analytic_growth_rate, # by passing keyword arguments to run_test, args becomes a Dict which can be used to # update the default inputs + # Make a copy to make sure nothing modifies the input Dicts defined in this test + # script. + test_input = deepcopy(test_input) + # Convert keyword arguments to a unique name name = test_input["run_name"] shortname = name diff --git a/moment_kinetics/test/velocity_integral_tests.jl b/moment_kinetics/test/velocity_integral_tests.jl index ae210954c..365f92905 100644 --- a/moment_kinetics/test/velocity_integral_tests.jl +++ b/moment_kinetics/test/velocity_integral_tests.jl @@ -45,6 +45,8 @@ function runtests() irank, Lvperp, discretization, fd_option, cheb_option, bc, adv_input, comm, "uniform") # create the coordinate struct 'x' + # This test runs effectively in serial, so use `ignore_MPI=true` to avoid + # errors due to communicators not being fully set up. vpa, vpa_spectral = define_coordinate(vpa_input; ignore_MPI=true) vperp, vperp_spectral = define_coordinate(vperp_input; ignore_MPI=true) vz, vz_spectral = define_coordinate(vz_input; ignore_MPI=true) diff --git a/moment_kinetics/test/wall_bc_tests.jl b/moment_kinetics/test/wall_bc_tests.jl index 175251802..6c46e294e 100644 --- a/moment_kinetics/test/wall_bc_tests.jl +++ b/moment_kinetics/test/wall_bc_tests.jl @@ -140,6 +140,10 @@ function run_test(test_input, expected_phi, tolerance; args...) # by passing keyword arguments to run_test, args becomes a Dict which can be used to # update the default inputs + # Make a copy to make sure nothing modifies the input Dicts defined in this test + # script. + test_input = deepcopy(test_input) + # Convert keyword arguments to a unique name name = test_input["run_name"] * ", with element spacing: " * test_input["z_element_spacing_option"] if length(args) > 0 diff --git a/plots_post_processing/plots_post_processing/src/plot_MMS_sequence.jl b/plots_post_processing/plots_post_processing/src/plot_MMS_sequence.jl index 6181f1209..84bfcf21b 100644 --- a/plots_post_processing/plots_post_processing/src/plot_MMS_sequence.jl +++ b/plots_post_processing/plots_post_processing/src/plot_MMS_sequence.jl @@ -14,16 +14,16 @@ using SpecialFunctions: erfi using LaTeXStrings # modules using ..post_processing_input: pp -using ..plots_post_processing: compare_charged_pdf_symbolic_test, compare_fields_symbolic_test +using ..plots_post_processing: compare_ion_pdf_symbolic_test, compare_fields_symbolic_test using ..plots_post_processing: compare_moments_symbolic_test, compare_neutral_pdf_symbolic_test -using ..plots_post_processing: allocate_global_zr_neutral_moments, allocate_global_zr_charged_moments +using ..plots_post_processing: allocate_global_zr_neutral_moments, allocate_global_zr_ion_moments using ..plots_post_processing: allocate_global_zr_fields, get_composition using ..plots_post_processing: get_coords_nelement, get_coords_ngrid using moment_kinetics.array_allocation: allocate_float using moment_kinetics.type_definitions: mk_float, mk_int using moment_kinetics.load_data: open_readonly_output_file using moment_kinetics.load_data: load_fields_data, load_pdf_data -using moment_kinetics.load_data: load_charged_particle_moments_data, load_neutral_particle_moments_data +using moment_kinetics.load_data: load_ion_moments_data, load_neutral_particle_moments_data using moment_kinetics.load_data: load_neutral_pdf_data, load_time_data, load_species_data using moment_kinetics.load_data: load_block_data, load_coordinate_data, load_input using moment_kinetics.load_data: read_distributed_zr_data!, construct_global_zr_coords @@ -198,7 +198,7 @@ function get_MMS_error_data(path_list,scan_type,scan_name) # allocate arrays to contain the global fields as a function of (z,r,t) phi, Ez, Er = allocate_global_zr_fields(z.n_global,r.n_global,ntime) density, parallel_flow, parallel_pressure, parallel_heat_flux, - thermal_speed = allocate_global_zr_charged_moments(z.n_global,r.n_global,n_ion_species,ntime) + thermal_speed = allocate_global_zr_ion_moments(z.n_global,r.n_global,n_ion_species,ntime) if n_neutral_species > 0 neutral_density, neutral_uz, neutral_pz, neutral_qz, neutral_thermal_speed = allocate_global_zr_neutral_moments(z.n_global,r.n_global,n_neutral_species,ntime) @@ -216,7 +216,7 @@ function get_MMS_error_data(path_list,scan_type,scan_name) read_distributed_zr_data!(phi,"phi",run_names,"moments",nbs,z.n,r.n,iskip) read_distributed_zr_data!(Ez,"Ez",run_names,"moments",nbs,z.n,r.n,iskip) read_distributed_zr_data!(Er,"Er",run_names,"moments",nbs,z.n,r.n,iskip) - # charged particle moments + # ion particle moments read_distributed_zr_data!(density,"density",run_names,"moments",nbs,z.n,r.n,iskip) read_distributed_zr_data!(parallel_flow,"parallel_flow",run_names,"moments",nbs,z.n,r.n,iskip) read_distributed_zr_data!(parallel_pressure,"parallel_pressure",run_names,"moments",nbs,z.n,r.n,iskip) @@ -298,7 +298,7 @@ function get_MMS_error_data(path_list,scan_type,scan_name) # use final time point for analysis ion_density_error_sequence[isim] = ion_density_error_t[end] - ion_pdf_error_t = compare_charged_pdf_symbolic_test(run_name,manufactured_solns_list,"ion", + ion_pdf_error_t = compare_ion_pdf_symbolic_test(run_name,manufactured_solns_list,"ion", L"\widetilde{f}_i",L"\widetilde{f}^{sym}_i",L"\sum || \widetilde{f}_i - \widetilde{f}_i^{sym} ||^2","pdf") ion_pdf_error_sequence[isim] = ion_pdf_error_t[end] diff --git a/plots_post_processing/plots_post_processing/src/plot_sequence.jl b/plots_post_processing/plots_post_processing/src/plot_sequence.jl index 6035b1cc2..be090814f 100644 --- a/plots_post_processing/plots_post_processing/src/plot_sequence.jl +++ b/plots_post_processing/plots_post_processing/src/plot_sequence.jl @@ -10,7 +10,7 @@ using Statistics: mean using SpecialFunctions: erfi using LaTeXStrings # modules -using ..plots_post_processing: allocate_global_zr_neutral_moments, allocate_global_zr_charged_moments +using ..plots_post_processing: allocate_global_zr_neutral_moments, allocate_global_zr_ion_moments using ..plots_post_processing: allocate_global_zr_fields#, get_coords_nelement using moment_kinetics.array_allocation: allocate_float using moment_kinetics.type_definitions: mk_float, mk_int diff --git a/plots_post_processing/plots_post_processing/src/plots_post_processing.jl b/plots_post_processing/plots_post_processing/src/plots_post_processing.jl index f2a451fda..972b92620 100644 --- a/plots_post_processing/plots_post_processing/src/plots_post_processing.jl +++ b/plots_post_processing/plots_post_processing/src/plots_post_processing.jl @@ -3,12 +3,11 @@ module plots_post_processing export analyze_and_plot_data -export compare_charged_pdf_symbolic_test export compare_moments_symbolic_test export compare_neutral_pdf_symbolic_test export compare_fields_symbolic_test export construct_global_zr_coords -export allocate_global_zr_charged_moments +export allocate_global_zr_ion_moments export allocate_global_zr_neutral_moments export allocate_global_zr_fields export get_coords_nelement @@ -43,9 +42,9 @@ using moment_kinetics.load_data: open_readonly_output_file, get_group, load_inpu load_time_data, construct_global_zr_coords using moment_kinetics.load_data: get_nranks using moment_kinetics.load_data: load_fields_data, load_pdf_data -using moment_kinetics.load_data: load_charged_particle_moments_data, +using moment_kinetics.load_data: load_ion_moments_data, load_neutral_particle_moments_data -using moment_kinetics.load_data: load_distributed_charged_pdf_slice, +using moment_kinetics.load_data: load_distributed_ion_pdf_slice, load_distributed_neutral_pdf_slice, iglobal_func using moment_kinetics.load_data: load_neutral_pdf_data using moment_kinetics.load_data: load_variable, read_distributed_zr_data! @@ -193,7 +192,7 @@ function allocate_global_zr_fields(nz_global,nr_global,ntime) return phi, Ez, Er end -function allocate_global_zr_charged_moments(nz_global,nr_global,n_ion_species,ntime) +function allocate_global_zr_ion_moments(nz_global,nr_global,n_ion_species,ntime) density = allocate_float(nz_global,nr_global,n_ion_species,ntime) parallel_flow = allocate_float(nz_global,nr_global,n_ion_species,ntime) parallel_pressure = allocate_float(nz_global,nr_global,n_ion_species,ntime) @@ -206,7 +205,7 @@ function allocate_global_zr_charged_moments(nz_global,nr_global,n_ion_species,nt return density, parallel_flow, parallel_pressure, perpendicular_pressure, parallel_heat_flux, thermal_speed, entropy_production, chodura_integral_lower, chodura_integral_upper end -function allocate_global_zr_charged_dfns(nvpa_global, nvperp_global, nz_global, nr_global, +function allocate_global_zr_ion_dfns(nvpa_global, nvperp_global, nz_global, nr_global, n_ion_species, ntime) f = allocate_float(nvpa_global, nvperp_global, nz_global, nr_global, n_ion_species, ntime) @@ -434,11 +433,11 @@ function analyze_and_plot_data(prefix...; run_index=nothing) Tuple(this_r.n_global for this_r ∈ r), ntime) density, parallel_flow, parallel_pressure, perpendicular_pressure, parallel_heat_flux, thermal_speed, entropy_production, chodura_integral_lower, chodura_integral_upper = - get_tuple_of_return_values(allocate_global_zr_charged_moments, + get_tuple_of_return_values(allocate_global_zr_ion_moments, Tuple(this_z.n_global for this_z ∈ z), Tuple(this_r.n_global for this_r ∈ r), n_ion_species, ntime) - if has_neutrals + if any(n_neutral_species .> 0) neutral_density, neutral_uz, neutral_pz, neutral_qz, neutral_thermal_speed = get_tuple_of_return_values(allocate_global_zr_neutral_moments, Tuple(this_z.n_global for this_z ∈ z), @@ -463,7 +462,7 @@ function analyze_and_plot_data(prefix...; run_index=nothing) nblocks, Tuple(this_z.n for this_z ∈ z), Tuple(this_r.n for this_r ∈ r), iskip) - # charged particle moments + # ion particle moments get_tuple_of_return_values(read_distributed_zr_data!, density, "density", run_names, "moments", nblocks, Tuple(this_z.n for this_z ∈ z), @@ -556,7 +555,7 @@ function analyze_and_plot_data(prefix...; run_index=nothing) perpendicular_pressure_at_pdf_timse, parallel_heat_flux_at_pdf_times, thermal_speed_at_pdf_times, entropy_production_at_pdf_times, chodura_integral_lower_at_pdf_times, chodura_integral_upper_at_pdf_times = - get_tuple_of_return_values(allocate_global_zr_charged_moments, + get_tuple_of_return_values(allocate_global_zr_ion_moments, Tuple(this_z.n_global for this_z ∈ z), Tuple(this_r.n_global for this_r ∈ r), n_ion_species, ntime_pdfs) @@ -581,7 +580,7 @@ function analyze_and_plot_data(prefix...; run_index=nothing) run_names, "dfns", nblocks, Tuple(this_z.n for this_z ∈ z), Tuple(this_r.n for this_r ∈ r), iskip_pdfs) - # charged particle moments + # ion particle moments get_tuple_of_return_values(read_distributed_zr_data!, density_at_pdf_times, "density", run_names, "dfns", nblocks, Tuple(this_z.n for this_z ∈ z), @@ -663,7 +662,7 @@ function analyze_and_plot_data(prefix...; run_index=nothing) (vr === nothing ? true : (this_vr.n == 1 for this_vr ∈ vr))...]) if is_1D1V && false # load full (vpa,z,r,species,t) particle distribution function (pdf) data - ff = get_tuple_of_return_values(load_distributed_charged_pdf_slice, run_names, + ff = get_tuple_of_return_values(load_distributed_ion_pdf_slice, run_names, nblocks, itime_min_pdfs:iskip_pdfs:itime_max_pdfs, n_ion_species, r, z, vperp, vpa) if has_neutrals @@ -1031,26 +1030,26 @@ function analyze_and_plot_data(prefix...; run_index=nothing) if !is_1D1V || true # make plots and animations of the phi, Ez and Er - plot_charged_moments_2D(density, parallel_flow, parallel_pressure, - perpendicular_pressure, thermal_speed, entropy_production, - chodura_integral_lower, chodura_integral_upper, time, - z_global.grid, r_global.grid, iz0, ir0, n_ion_species, - itime_min, itime_max, nwrite_movie, run_name_label, pp) + plot_ion_moments_2D(density, parallel_flow, parallel_pressure, + perpendicular_pressure, thermal_speed, entropy_production, + chodura_integral_lower, chodura_integral_upper, time, + z_global.grid, r_global.grid, iz0, ir0, n_ion_species, + itime_min, itime_max, nwrite_movie, run_name_label, pp) # make plots and animations of the phi, Ez and Er plot_fields_2D(phi, Ez, Er, time, z_global.grid, r_global.grid, iz0, ir0, itime_min, itime_max, nwrite_movie, run_name_label, pp, "") # load full (vpa,z,r,species,t) particle distribution function (pdf) data spec_type = "ion" - plot_charged_pdf(run_name, run_name_label, vpa, vperp, z_global, r_global, z, r, - ivpa0, ivperp0, iz0, ir0, spec_type, n_ion_species, ntime_pdfs, - nblocks, itime_min_pdfs, itime_max_pdfs, iskip_pdfs, - nwrite_movie_pdfs, pp) + plot_ion_pdf(run_name, run_name_label, vpa, vperp, z_global, r_global, z, r, + ivpa0, ivperp0, iz0, ir0, spec_type, n_ion_species, ntime_pdfs, + nblocks, itime_min_pdfs, itime_max_pdfs, iskip_pdfs, + nwrite_movie_pdfs, pp) Maxwellian_diagnostic = true if Maxwellian_diagnostic pressure = copy(parallel_pressure) @. pressure = (2.0*perpendicular_pressure + parallel_pressure)/3.0 - ff = load_distributed_charged_pdf_slice(run_name, nblocks, 1:ntime, n_ion_species, r, + ff = load_distributed_ion_pdf_slice(run_name, nblocks, 1:ntime, n_ion_species, r, z, vperp, vpa; iz=iz0, ir=ir0) plot_Maxwellian_diagnostic(ff[:,:,:,:], density[iz0,ir0,:,:], parallel_flow[iz0,ir0,:,:], thermal_speed[iz0,ir0,:,:], vpa.grid, vpa.wgts, @@ -1066,9 +1065,9 @@ function analyze_and_plot_data(prefix...; run_index=nothing) end # plot ion pdf data near the wall boundary if pp.plot_wall_pdf - plot_charged_pdf_2D_at_wall(run_name, run_name_label, r_global, z_global, - nblocks, n_ion_species, r, z, vperp, vpa, - ntime_pdfs) + plot_ion_pdf_2D_at_wall(run_name, run_name_label, r_global, z_global, + nblocks, n_ion_species, r, z, vperp, vpa, + ntime_pdfs) end end @@ -1157,7 +1156,7 @@ function analyze_and_plot_data(prefix...; run_index=nothing) compare_moments_symbolic_test(run_name_label,thermal_speed,vthi_sym,"ion",z_global.grid,r_global.grid,time,z_global.n,r_global.n,ntime, L"\widetilde{v}_{th,i}",L"\widetilde{v}_{th,i}^{sym}",L"\varepsilon(\widetilde{v}_{th,i})","vthi") - compare_charged_pdf_symbolic_test(run_name_label,manufactured_solns_list,"ion", + compare_ion_pdf_symbolic_test(run_name_label,manufactured_solns_list,"ion", L"\widetilde{f}_i",L"\widetilde{f}^{sym}_i",L"\varepsilon(\widetilde{f}_i)","pdf") if n_neutral_species > 0 # neutral test @@ -1253,7 +1252,7 @@ function calculate_differences(prefix...) ntime) density, parallel_flow, parallel_pressure, parallel_heat_flux, thermal_speed, chodura_integral_lower, chodura_integral_upper = - get_tuple_of_return_values(allocate_global_zr_charged_moments, + get_tuple_of_return_values(allocate_global_zr_ion_moments, Tuple(this_z.n_global for this_z ∈ z), Tuple(this_r.n_global for this_r ∈ r), n_ion_species, ntime) @@ -1282,7 +1281,7 @@ function calculate_differences(prefix...) nblocks, Tuple(this_z.n for this_z ∈ z), Tuple(this_r.n for this_r ∈ r)) - # charged particle moments + # ion particle moments get_tuple_of_return_values(read_distributed_zr_data!, density, "density", run_names, "moments", nblocks, Tuple(this_z.n for this_z ∈ z), @@ -1358,7 +1357,7 @@ function calculate_differences(prefix...) density_at_pdf_times, parallel_flow_at_pdf_times, parallel_pressure_at_pdf_times, parallel_heat_flux_at_pdf_times, thermal_speed_at_pdf_times, chodura_integral_lower_at_pdf_times, chodura_integral_upper_at_pdf_times = - get_tuple_of_return_values(allocate_global_zr_charged_moments, + get_tuple_of_return_values(allocate_global_zr_ion_moments, Tuple(this_z.n_global for this_z ∈ z), Tuple(this_r.n_global for this_r ∈ r), n_ion_species, ntime_pdfs) @@ -1383,7 +1382,7 @@ function calculate_differences(prefix...) run_names, "dfns", nblocks, Tuple(this_z.n for this_z ∈ z), Tuple(this_r.n for this_r ∈ r)) - # charged particle moments + # ion moments get_tuple_of_return_values(read_distributed_zr_data!, density_at_pdf_times, "density", run_names, "dfns", nblocks, Tuple(this_z.n for this_z ∈ z), @@ -1431,7 +1430,7 @@ function calculate_differences(prefix...) end # load full (vpa,z,r,species,t) particle distribution function (pdf) data - ff = get_tuple_of_return_values(load_distributed_charged_pdf_slice, run_names, + ff = get_tuple_of_return_values(load_distributed_ion_pdf_slice, run_names, nblocks, Tuple(1:nt for nt ∈ ntime_pdfs), n_ion_species, r, z, vperp, vpa) if maximum(n_neutral_species) > 0 @@ -2658,7 +2657,7 @@ function compare_moments_symbolic_test(run_name,moment,moment_sym,spec_string,z, end -function compare_charged_pdf_symbolic_test(run_name,manufactured_solns_list,spec_string, +function compare_ion_pdf_symbolic_test(run_name,manufactured_solns_list,spec_string, pdf_label,pdf_sym_label,norm_label,file_string) fid = open_readonly_output_file(run_name,"dfns", printout=false) # load block data on iblock=0 @@ -2672,7 +2671,7 @@ function compare_charged_pdf_symbolic_test(run_name,manufactured_solns_list,spec # load time data (unique to pdf, may differ to moment values depending on user nwrite_dfns value) ntime, time, _ = load_time_data(fid, printout=false) close(fid) - # get the charged particle pdf + # get the ion pdf dfni_func = manufactured_solns_list.dfni_func is = 1 # only one species supported currently @@ -2754,7 +2753,7 @@ function compare_neutral_pdf_symbolic_test(run_name,manufactured_solns_list,spec # load time data (unique to pdf, may differ to moment values depending on user nwrite_dfns value) ntime, time, _ = load_time_data(fid, printout=false) close(fid) - # get the charged particle pdf + # get the ion pdf dfnn_func = manufactured_solns_list.dfnn_func is = 1 # only one species supported currently @@ -2837,7 +2836,7 @@ end """ plots various slices of the ion pdf (1d and 2d, stills and animations) """ -function plot_charged_pdf(run_name, run_name_label, vpa, vperp, z, r, z_local, r_local, +function plot_ion_pdf(run_name, run_name_label, vpa, vperp, z, r, z_local, r_local, ivpa0, ivperp0, iz0, ir0, spec_type, n_species, n_time_pdfs, nblocks, itime_min, itime_max, iskip, nwrite_movie, pp) @@ -2860,7 +2859,7 @@ function plot_charged_pdf(run_name, run_name_label, vpa, vperp, z, r, z_local, r end # make a gif animation of f(vpa,z,t) at a given (vperp,r) location if pp.animate_f_vs_vpa_z - pdf = load_distributed_charged_pdf_slice(run_name, nblocks, + pdf = load_distributed_ion_pdf_slice(run_name, nblocks, itime_min:iskip:itime_max, n_species, r_local, z_local, vperp, vpa; ivperp=ivperp0, ir=ir0) @@ -2878,10 +2877,10 @@ function plot_charged_pdf(run_name, run_name_label, vpa, vperp, z, r, z_local, r end # make a gif animation of f(vpa,r,t) at a given (vperp,z) location if pp.animate_f_vs_vpa_r - pdf = load_distributed_charged_pdf_slice(run_name, nblocks, - itime_min:iskip:itime_max, n_species, - r_local, z_local, vperp, vpa; - ivperp=ivperp0, iz=iz0) + pdf = load_distributed_ion_pdf_slice(run_name, nblocks, + itime_min:iskip:itime_max, n_species, + r_local, z_local, vperp, vpa; + ivperp=ivperp0, iz=iz0) for is ∈ 1:n_species anim = @animate for i ∈ itime_min:nwrite_movie:itime_max @views heatmap(r.grid, vpa.grid, pdf[:,:,is,i], xlabel="r", ylabel="vpa", c = :deep, interpolation = :cubic) @@ -2896,7 +2895,7 @@ function plot_charged_pdf(run_name, run_name_label, vpa, vperp, z, r, z_local, r end # make a gif animation of f(vperp,z,t) at a given (vpa,r) location if pp.animate_f_vs_vperp_z - pdf = load_distributed_charged_pdf_slice(run_name, nblocks, + pdf = load_distributed_ion_pdf_slice(run_name, nblocks, itime_min:iskip:itime_max, n_species, r_local, z_local, vperp, vpa; ivpa=ivpa0, ir=ir0) @@ -2910,7 +2909,7 @@ function plot_charged_pdf(run_name, run_name_label, vpa, vperp, z, r, z_local, r end # make a gif animation of f(vperp,r,t) at a given (vpa,z) location if pp.animate_f_vs_vperp_r - pdf = load_distributed_charged_pdf_slice(run_name, nblocks, + pdf = load_distributed_ion_pdf_slice(run_name, nblocks, itime_min:iskip:itime_max, n_species, r_local, z_local, vperp, vpa; ivpa=ivpa0, iz=iz0) @@ -2924,7 +2923,7 @@ function plot_charged_pdf(run_name, run_name_label, vpa, vperp, z, r, z_local, r end # make a gif animation of f(vpa,vperp,t) at a given (z,r) location if pp.animate_f_vs_vperp_vpa - pdf = load_distributed_charged_pdf_slice(run_name, nblocks, + pdf = load_distributed_ion_pdf_slice(run_name, nblocks, itime_min:iskip:itime_max, n_species, r_local, z_local, vperp, vpa; iz=iz0, ir=ir0) @@ -2972,10 +2971,10 @@ function plot_charged_pdf(run_name, run_name_label, vpa, vperp, z, r, z_local, r end # make a gif animation of f(z,r,t) at a given (vpa,vperp) location if pp.animate_f_vs_r_z - pdf = load_distributed_charged_pdf_slice(run_name, nblocks, - itime_min:iskip:itime_max, n_species, - r_local, z_local, vperp, vpa; ivpa=ivpa0, - ivperp=ivperp0) + pdf = load_distributed_ion_pdf_slice(run_name, nblocks, + itime_min:iskip:itime_max, n_species, + r_local, z_local, vperp, vpa; ivpa=ivpa0, + ivperp=ivperp0) for is ∈ 1:n_species anim = @animate for i ∈ itime_min:nwrite_movie:itime_max @views heatmap(r.grid, z.grid, pdf[:,:,is,i], xlabel="r", ylabel="z", c = :deep, interpolation = :cubic) @@ -3154,14 +3153,14 @@ function plot_fields_2D(phi, Ez, Er, time, z, r, iz0, ir0, println("done.") end -function plot_charged_moments_2D(density, parallel_flow, parallel_pressure, +function plot_ion_moments_2D(density, parallel_flow, parallel_pressure, perpendicular_pressure, thermal_speed, entropy_production,chodura_integral_lower, chodura_integral_upper, time, z, r, iz0, ir0, n_ion_species, itime_min, itime_max, nwrite_movie, run_name, pp) nr = size(r,1) ntime = size(time,1) - print("Plotting charged moments data...") + print("Plotting ion moments data...") for is in 1:n_ion_species description = "_ion_spec"*string(is)*"_" # the density @@ -3427,9 +3426,9 @@ function plot_Maxwellian_diagnostic(ff, density, parallel_flow, thermal_speed, v end return nothing end -function plot_charged_pdf_2D_at_wall(run_name, run_name_label, r_global, z_global, +function plot_ion_pdf_2D_at_wall(run_name, run_name_label, r_global, z_global, nblocks, n_ion_species, r, z, vperp, vpa, ntime) - print("Plotting charged pdf data at wall boundaries...") + print("Plotting ion pdf data at wall boundaries...") # plot a thermal vpa on line plots ivpa0 = floor(mk_int,vpa.n/3) @@ -3441,10 +3440,10 @@ function plot_charged_pdf_2D_at_wall(run_name, run_name_label, r_global, z_globa # Note only need final time point, so only load the one time point itime0 = 1 # pdf at lower wall - pdf_lower = load_distributed_charged_pdf_slice(run_name, nblocks, ntime:ntime, n_ion_species, r, + pdf_lower = load_distributed_ion_pdf_slice(run_name, nblocks, ntime:ntime, n_ion_species, r, z, vperp, vpa; iz=1) # pdf at upper wall - pdf_upper = load_distributed_charged_pdf_slice(run_name, nblocks, ntime:ntime, n_ion_species, r, + pdf_upper = load_distributed_ion_pdf_slice(run_name, nblocks, ntime:ntime, n_ion_species, r, z, vperp, vpa; iz=z.n_global) for (pdf, zlabel) ∈ ((pdf_lower, "wall-"), (pdf_upper, "wall+")) for is in 1:n_ion_species @@ -3474,7 +3473,7 @@ function plot_charged_pdf_2D_at_wall(run_name, run_name_label, r_global, z_globa outfile = string(run_name_label, "_pdf(vpa,vperp,iz_"*zlabel*",ir0)"*description*"vs_vperp_vpa.pdf") trysavefig(outfile) - # Skip this because load_distributed_charged_pdf_slice() currently only + # Skip this because load_distributed_ion_pdf_slice() currently only # handles selecting a single value like `iz=1`, not a sub-slice like # `iz=1:n_local`, so we only have data for one point in z here, so we can't # plot vs z. @@ -3486,7 +3485,7 @@ function plot_charged_pdf_2D_at_wall(run_name, run_name_label, r_global, z_globa # plot f(ivpa0,ivperp0,z,r,is,itime) near the wall if r.n > 1 - # Skip this because load_distributed_charged_pdf_slice() currently only + # Skip this because load_distributed_ion_pdf_slice() currently only # handles selecting a single value like `iz=1`, not a sub-slice like # `iz=1:n_local`, so we only have data for one point in z here, so we # can't plot vs z. diff --git a/publication_inputs/2023_EFTC_jto-poster/wall-bc/wall-bc_recyclefraction0.5.toml b/publication_inputs/2023_EFTC_jto-poster/wall-bc/wall-bc_recyclefraction0.5.toml index ec657ade1..8943ac402 100644 --- a/publication_inputs/2023_EFTC_jto-poster/wall-bc/wall-bc_recyclefraction0.5.toml +++ b/publication_inputs/2023_EFTC_jto-poster/wall-bc/wall-bc_recyclefraction0.5.toml @@ -82,8 +82,14 @@ z_width = 0.125 source_strength = 2.0 source_T = 2.0 -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 1.0e-1 #vpa_dissipation_coefficient = 1.0e-2 #vpa_dissipation_coefficient = 1.0e-3 #force_minimum_pdf_value = 0.0 + +[neutral_numerical_dissipation] +#vz_dissipation_coefficient = 1.0e-1 +#vz_dissipation_coefficient = 1.0e-2 +#vz_dissipation_coefficient = 1.0e-3 +#force_minimum_pdf_value = 0.0 diff --git a/publication_inputs/2023_EFTC_jto-poster/wall-bc/wall-bc_recyclefraction0.5_split1.toml b/publication_inputs/2023_EFTC_jto-poster/wall-bc/wall-bc_recyclefraction0.5_split1.toml index c819bb71c..f7a1484d7 100644 --- a/publication_inputs/2023_EFTC_jto-poster/wall-bc/wall-bc_recyclefraction0.5_split1.toml +++ b/publication_inputs/2023_EFTC_jto-poster/wall-bc/wall-bc_recyclefraction0.5_split1.toml @@ -82,8 +82,14 @@ z_width = 0.125 source_strength = 2.0 source_T = 2.0 -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 1.0e-1 #vpa_dissipation_coefficient = 1.0e-2 #vpa_dissipation_coefficient = 1.0e-3 #force_minimum_pdf_value = 0.0 + +[neutral_numerical_dissipation] +#vz_dissipation_coefficient = 1.0e-1 +#vz_dissipation_coefficient = 1.0e-2 +#vz_dissipation_coefficient = 1.0e-3 +#force_minimum_pdf_value = 0.0 diff --git a/publication_inputs/2023_EFTC_jto-poster/wall-bc/wall-bc_recyclefraction0.5_split2.toml b/publication_inputs/2023_EFTC_jto-poster/wall-bc/wall-bc_recyclefraction0.5_split2.toml index 702fd6b0c..ef1a221cb 100644 --- a/publication_inputs/2023_EFTC_jto-poster/wall-bc/wall-bc_recyclefraction0.5_split2.toml +++ b/publication_inputs/2023_EFTC_jto-poster/wall-bc/wall-bc_recyclefraction0.5_split2.toml @@ -82,8 +82,14 @@ z_width = 0.125 source_strength = 2.0 source_T = 2.0 -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 1.0e-1 #vpa_dissipation_coefficient = 1.0e-2 #vpa_dissipation_coefficient = 1.0e-3 force_minimum_pdf_value = 0.0 + +[neutral_numerical_dissipation] +#vz_dissipation_coefficient = 1.0e-1 +#vz_dissipation_coefficient = 1.0e-2 +#vz_dissipation_coefficient = 1.0e-3 +force_minimum_pdf_value = 0.0 diff --git a/publication_inputs/2023_EFTC_jto-poster/wall-bc/wall-bc_recyclefraction0.5_split3.toml b/publication_inputs/2023_EFTC_jto-poster/wall-bc/wall-bc_recyclefraction0.5_split3.toml index cc6c15f4b..79c01a3be 100644 --- a/publication_inputs/2023_EFTC_jto-poster/wall-bc/wall-bc_recyclefraction0.5_split3.toml +++ b/publication_inputs/2023_EFTC_jto-poster/wall-bc/wall-bc_recyclefraction0.5_split3.toml @@ -82,8 +82,14 @@ z_width = 0.125 source_strength = 2.0 source_T = 2.0 -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 1.0e-1 #vpa_dissipation_coefficient = 1.0e-2 #vpa_dissipation_coefficient = 1.0e-3 force_minimum_pdf_value = 0.0 + +[ion_numerical_dissipation] +#vz_dissipation_coefficient = 1.0e-1 +#vz_dissipation_coefficient = 1.0e-2 +#vz_dissipation_coefficient = 1.0e-3 +force_minimum_pdf_value = 0.0 diff --git a/runs/1D-mirror_MMS_new_nel_r_1_z_16_vpa_16_vperp_8_diss.toml b/runs/1D-mirror_MMS_new_nel_r_1_z_16_vpa_16_vperp_8_diss.toml index 45d596904..9a475346b 100644 --- a/runs/1D-mirror_MMS_new_nel_r_1_z_16_vpa_16_vperp_8_diss.toml +++ b/runs/1D-mirror_MMS_new_nel_r_1_z_16_vpa_16_vperp_8_diss.toml @@ -91,7 +91,7 @@ vzeta_discretization = "chebyshev_pseudospectral" use_vpabar_in_mms_dfni=true alpha_switch=1.0 type="default" -[numerical_dissipation] +[ion_numerical_dissipation] vpa_dissipation_coefficient = 0.001 vperp_dissipation_coefficient = 0.001 #z_dissipation_coefficient = 0.1 diff --git a/runs/1D-mirror_MMS_new_nel_r_1_z_6_vpa_6_vperp_3_diss.toml b/runs/1D-mirror_MMS_new_nel_r_1_z_6_vpa_6_vperp_3_diss.toml index a465d248c..347dd2b04 100644 --- a/runs/1D-mirror_MMS_new_nel_r_1_z_6_vpa_6_vperp_3_diss.toml +++ b/runs/1D-mirror_MMS_new_nel_r_1_z_6_vpa_6_vperp_3_diss.toml @@ -92,7 +92,7 @@ vzeta_discretization = "chebyshev_pseudospectral" use_vpabar_in_mms_dfni=true alpha_switch=1.0 type="default" -[numerical_dissipation] +[ion_numerical_dissipation] vpa_dissipation_coefficient = 0.001 vperp_dissipation_coefficient = 0.001 #z_dissipation_coefficient = 0.1 diff --git a/runs/1D-mirror_MMS_ngrid_9_nel_r_1_z_16_vpa_16_vperp_8_diss.toml b/runs/1D-mirror_MMS_ngrid_9_nel_r_1_z_16_vpa_16_vperp_8_diss.toml index 6a89faab1..cb8ce6cf2 100644 --- a/runs/1D-mirror_MMS_ngrid_9_nel_r_1_z_16_vpa_16_vperp_8_diss.toml +++ b/runs/1D-mirror_MMS_ngrid_9_nel_r_1_z_16_vpa_16_vperp_8_diss.toml @@ -87,7 +87,7 @@ vzeta_discretization = "chebyshev_pseudospectral" use_vpabar_in_mms_dfni=true alpha_switch=1.0 type="default" -[numerical_dissipation] +[ion_numerical_dissipation] vpa_dissipation_coefficient = 0.001 vperp_dissipation_coefficient = 0.001 #z_dissipation_coefficient = 0.1 diff --git a/runs/1D-mirror_MMS_ngrid_9_nel_r_1_z_32_vpa_32_vperp_16_diss.toml b/runs/1D-mirror_MMS_ngrid_9_nel_r_1_z_32_vpa_32_vperp_16_diss.toml index a90350f22..6d9c31403 100644 --- a/runs/1D-mirror_MMS_ngrid_9_nel_r_1_z_32_vpa_32_vperp_16_diss.toml +++ b/runs/1D-mirror_MMS_ngrid_9_nel_r_1_z_32_vpa_32_vperp_16_diss.toml @@ -87,7 +87,7 @@ vzeta_discretization = "chebyshev_pseudospectral" use_vpabar_in_mms_dfni=true alpha_switch=1.0 type="default" -[numerical_dissipation] +[ion_numerical_dissipation] vpa_dissipation_coefficient = 0.001 vperp_dissipation_coefficient = 0.001 #z_dissipation_coefficient = 0.1 diff --git a/runs/1D-mirror_MMS_ngrid_9_nel_r_1_z_4_vpa_4_vperp_2_diss.toml b/runs/1D-mirror_MMS_ngrid_9_nel_r_1_z_4_vpa_4_vperp_2_diss.toml index 92a8aa331..c0ec6f8f5 100644 --- a/runs/1D-mirror_MMS_ngrid_9_nel_r_1_z_4_vpa_4_vperp_2_diss.toml +++ b/runs/1D-mirror_MMS_ngrid_9_nel_r_1_z_4_vpa_4_vperp_2_diss.toml @@ -87,7 +87,7 @@ vzeta_discretization = "chebyshev_pseudospectral" use_vpabar_in_mms_dfni=true alpha_switch=1.0 type="default" -[numerical_dissipation] +[ion_numerical_dissipation] vpa_dissipation_coefficient = 0.001 vperp_dissipation_coefficient = 0.001 #z_dissipation_coefficient = 0.1 diff --git a/runs/1D-mirror_MMS_ngrid_9_nel_r_1_z_64_vpa_64_vperp_32_diss.toml b/runs/1D-mirror_MMS_ngrid_9_nel_r_1_z_64_vpa_64_vperp_32_diss.toml index 5f63e0aec..dff2bf452 100644 --- a/runs/1D-mirror_MMS_ngrid_9_nel_r_1_z_64_vpa_64_vperp_32_diss.toml +++ b/runs/1D-mirror_MMS_ngrid_9_nel_r_1_z_64_vpa_64_vperp_32_diss.toml @@ -87,7 +87,7 @@ vzeta_discretization = "chebyshev_pseudospectral" use_vpabar_in_mms_dfni=true alpha_switch=1.0 type="default" -[numerical_dissipation] +[ion_numerical_dissipation] vpa_dissipation_coefficient = 0.001 vperp_dissipation_coefficient = 0.0 #z_dissipation_coefficient = 0.1 diff --git a/runs/1D-mirror_MMS_ngrid_9_nel_r_1_z_8_vpa_8_vperp_4_diss.toml b/runs/1D-mirror_MMS_ngrid_9_nel_r_1_z_8_vpa_8_vperp_4_diss.toml index c8e8df9ee..a5be20d0d 100644 --- a/runs/1D-mirror_MMS_ngrid_9_nel_r_1_z_8_vpa_8_vperp_4_diss.toml +++ b/runs/1D-mirror_MMS_ngrid_9_nel_r_1_z_8_vpa_8_vperp_4_diss.toml @@ -87,7 +87,7 @@ vzeta_discretization = "chebyshev_pseudospectral" use_vpabar_in_mms_dfni=true alpha_switch=1.0 type="default" -[numerical_dissipation] +[ion_numerical_dissipation] vpa_dissipation_coefficient = 0.001 vperp_dissipation_coefficient = 0.001 #z_dissipation_coefficient = 0.1 diff --git a/runs/1D-wall_MMS_new_nel_r_1_z_16_vpa_16_vperp_1_diss.toml b/runs/1D-wall_MMS_new_nel_r_1_z_16_vpa_16_vperp_1_diss.toml index d6851c2bf..e28b4b068 100644 --- a/runs/1D-wall_MMS_new_nel_r_1_z_16_vpa_16_vperp_1_diss.toml +++ b/runs/1D-wall_MMS_new_nel_r_1_z_16_vpa_16_vperp_1_diss.toml @@ -87,7 +87,7 @@ split_operators = false use_vpabar_in_mms_dfni=true alpha_switch=1.0 type="default" -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 0.1 #z_dissipation_coefficient = 0.1 r_dissipation_coefficient = 0.0 diff --git a/runs/1D-wall_MMS_new_nel_r_1_z_16_vpa_16_vperp_1_krook.toml b/runs/1D-wall_MMS_new_nel_r_1_z_16_vpa_16_vperp_1_krook.toml index 2edf54dc2..f45c8828d 100644 --- a/runs/1D-wall_MMS_new_nel_r_1_z_16_vpa_16_vperp_1_krook.toml +++ b/runs/1D-wall_MMS_new_nel_r_1_z_16_vpa_16_vperp_1_krook.toml @@ -88,7 +88,7 @@ split_operators = false use_vpabar_in_mms_dfni=true alpha_switch=1.0 type="default" -[numerical_dissipation] +[ion_numerical_dissipation] vpa_dissipation_coefficient = -1.0 z_dissipation_coefficient = -1.0 r_dissipation_coefficient = -1.0 diff --git a/runs/1D-wall_MMS_new_nel_r_1_z_16_vpa_8_vperp_8_krook.toml b/runs/1D-wall_MMS_new_nel_r_1_z_16_vpa_8_vperp_8_krook.toml index aa6b849d5..9ec02fede 100644 --- a/runs/1D-wall_MMS_new_nel_r_1_z_16_vpa_8_vperp_8_krook.toml +++ b/runs/1D-wall_MMS_new_nel_r_1_z_16_vpa_8_vperp_8_krook.toml @@ -88,7 +88,7 @@ split_operators = false use_vpabar_in_mms_dfni=true alpha_switch=1.0 type="default" -[numerical_dissipation] +[ion_numerical_dissipation] vpa_dissipation_coefficient = -1.0 z_dissipation_coefficient = -1.0 r_dissipation_coefficient = -1.0 diff --git a/runs/2D-mirror_MMS_ngrid_5_nel_r_16_z_16_vpa_16_vperp_8_diss.toml b/runs/2D-mirror_MMS_ngrid_5_nel_r_16_z_16_vpa_16_vperp_8_diss.toml index a300c9a56..cf3eb5d19 100644 --- a/runs/2D-mirror_MMS_ngrid_5_nel_r_16_z_16_vpa_16_vperp_8_diss.toml +++ b/runs/2D-mirror_MMS_ngrid_5_nel_r_16_z_16_vpa_16_vperp_8_diss.toml @@ -87,7 +87,7 @@ vzeta_discretization = "chebyshev_pseudospectral" use_vpabar_in_mms_dfni=true alpha_switch=1.0 type="default" -[numerical_dissipation] +[ion_numerical_dissipation] vpa_dissipation_coefficient = 0.001 vperp_dissipation_coefficient = 0.001 #z_dissipation_coefficient = 0.1 diff --git a/runs/2D-mirror_MMS_ngrid_5_nel_r_32_z_32_vpa_32_vperp_16_diss.toml b/runs/2D-mirror_MMS_ngrid_5_nel_r_32_z_32_vpa_32_vperp_16_diss.toml index c98077d3a..5bd48c14b 100644 --- a/runs/2D-mirror_MMS_ngrid_5_nel_r_32_z_32_vpa_32_vperp_16_diss.toml +++ b/runs/2D-mirror_MMS_ngrid_5_nel_r_32_z_32_vpa_32_vperp_16_diss.toml @@ -87,7 +87,7 @@ vzeta_discretization = "chebyshev_pseudospectral" use_vpabar_in_mms_dfni=true alpha_switch=1.0 type="default" -[numerical_dissipation] +[ion_numerical_dissipation] vpa_dissipation_coefficient = 0.001 vperp_dissipation_coefficient = 0.001 #z_dissipation_coefficient = 0.1 diff --git a/runs/2D-mirror_MMS_ngrid_5_nel_r_4_z_4_vpa_4_vperp_2_diss.toml b/runs/2D-mirror_MMS_ngrid_5_nel_r_4_z_4_vpa_4_vperp_2_diss.toml index 3ec12c9fe..b13205d70 100644 --- a/runs/2D-mirror_MMS_ngrid_5_nel_r_4_z_4_vpa_4_vperp_2_diss.toml +++ b/runs/2D-mirror_MMS_ngrid_5_nel_r_4_z_4_vpa_4_vperp_2_diss.toml @@ -87,7 +87,7 @@ vzeta_discretization = "chebyshev_pseudospectral" use_vpabar_in_mms_dfni=true alpha_switch=1.0 type="default" -[numerical_dissipation] +[ion_numerical_dissipation] vpa_dissipation_coefficient = 0.001 vperp_dissipation_coefficient = 0.001 #z_dissipation_coefficient = 0.1 diff --git a/runs/2D-mirror_MMS_ngrid_5_nel_r_8_z_8_vpa_8_vperp_4_diss.toml b/runs/2D-mirror_MMS_ngrid_5_nel_r_8_z_8_vpa_8_vperp_4_diss.toml index e936ef06e..d011761cb 100644 --- a/runs/2D-mirror_MMS_ngrid_5_nel_r_8_z_8_vpa_8_vperp_4_diss.toml +++ b/runs/2D-mirror_MMS_ngrid_5_nel_r_8_z_8_vpa_8_vperp_4_diss.toml @@ -87,7 +87,7 @@ vzeta_discretization = "chebyshev_pseudospectral" use_vpabar_in_mms_dfni=true alpha_switch=1.0 type="default" -[numerical_dissipation] +[ion_numerical_dissipation] vpa_dissipation_coefficient = 0.001 vperp_dissipation_coefficient = 0.001 #z_dissipation_coefficient = 0.1 diff --git a/runs/2D-wall_MMS_nel_r_16_z_16_vpa_16_vperp_1_diss.toml b/runs/2D-wall_MMS_nel_r_16_z_16_vpa_16_vperp_1_diss.toml index b3aa7eff8..3fb456895 100644 --- a/runs/2D-wall_MMS_nel_r_16_z_16_vpa_16_vperp_1_diss.toml +++ b/runs/2D-wall_MMS_nel_r_16_z_16_vpa_16_vperp_1_diss.toml @@ -78,7 +78,7 @@ nwrite_dfns = 200 n_rk_stages = 4 split_operators = false -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 0.1 #z_dissipation_coefficient = 0.1 r_dissipation_coefficient = 0.01 diff --git a/runs/2D-wall_MMS_nel_r_2_z_2_vpa_16_vperp_1_diss.toml b/runs/2D-wall_MMS_nel_r_2_z_2_vpa_16_vperp_1_diss.toml index eaeecff96..ee1765bf4 100644 --- a/runs/2D-wall_MMS_nel_r_2_z_2_vpa_16_vperp_1_diss.toml +++ b/runs/2D-wall_MMS_nel_r_2_z_2_vpa_16_vperp_1_diss.toml @@ -78,7 +78,7 @@ nwrite_dfns = 200 n_rk_stages = 4 split_operators = false -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 0.1 #z_dissipation_coefficient = 0.1 r_dissipation_coefficient = 0.01 diff --git a/runs/2D-wall_MMS_nel_r_32_z_32_vpa_16_vperp_1_diss.toml b/runs/2D-wall_MMS_nel_r_32_z_32_vpa_16_vperp_1_diss.toml index 19c6593b8..38bc4655a 100644 --- a/runs/2D-wall_MMS_nel_r_32_z_32_vpa_16_vperp_1_diss.toml +++ b/runs/2D-wall_MMS_nel_r_32_z_32_vpa_16_vperp_1_diss.toml @@ -87,7 +87,7 @@ split_operators = false use_vpabar_in_mms_dfni=true alpha_switch=1.0 type="default" -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 0.1 #z_dissipation_coefficient = 0.1 r_dissipation_coefficient = 0.1 diff --git a/runs/2D-wall_MMS_nel_r_32_z_32_vpa_16_vperp_1_diss5.toml b/runs/2D-wall_MMS_nel_r_32_z_32_vpa_16_vperp_1_diss5.toml index 6b9bfc86d..16c5ca77a 100644 --- a/runs/2D-wall_MMS_nel_r_32_z_32_vpa_16_vperp_1_diss5.toml +++ b/runs/2D-wall_MMS_nel_r_32_z_32_vpa_16_vperp_1_diss5.toml @@ -78,7 +78,7 @@ nwrite_dfns = 200 n_rk_stages = 4 split_operators = false -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 0.1 #z_dissipation_coefficient = 0.1 r_dissipation_coefficient = 0.01 diff --git a/runs/2D-wall_MMS_nel_r_4_z_4_vpa_16_vperp_1_diss.toml b/runs/2D-wall_MMS_nel_r_4_z_4_vpa_16_vperp_1_diss.toml index 3307b52b2..2211ef9a6 100644 --- a/runs/2D-wall_MMS_nel_r_4_z_4_vpa_16_vperp_1_diss.toml +++ b/runs/2D-wall_MMS_nel_r_4_z_4_vpa_16_vperp_1_diss.toml @@ -78,7 +78,7 @@ nwrite_dfns = 200 n_rk_stages = 4 split_operators = false -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 0.1 #z_dissipation_coefficient = 0.1 r_dissipation_coefficient = 0.01 diff --git a/runs/2D-wall_MMS_nel_r_8_z_8_vpa_16_vperp_1_diss.toml b/runs/2D-wall_MMS_nel_r_8_z_8_vpa_16_vperp_1_diss.toml index 78f3e95b7..095f8d86e 100644 --- a/runs/2D-wall_MMS_nel_r_8_z_8_vpa_16_vperp_1_diss.toml +++ b/runs/2D-wall_MMS_nel_r_8_z_8_vpa_16_vperp_1_diss.toml @@ -78,7 +78,7 @@ nwrite_dfns = 200 n_rk_stages = 4 split_operators = false -[numerical_dissipation] +[ion_numerical_dissipation] #vpa_dissipation_coefficient = 0.1 #z_dissipation_coefficient = 0.1 r_dissipation_coefficient = 0.01 diff --git a/runs/2V-evolve_ngrid_3_nel_r_1_z_1_vpa_6_vperp_3_fkpl_test.toml b/runs/2V-evolve_ngrid_3_nel_r_1_z_1_vpa_6_vperp_3_fkpl_test.toml index 3d9efefcb..543739bed 100644 --- a/runs/2V-evolve_ngrid_3_nel_r_1_z_1_vpa_6_vperp_3_fkpl_test.toml +++ b/runs/2V-evolve_ngrid_3_nel_r_1_z_1_vpa_6_vperp_3_fkpl_test.toml @@ -90,7 +90,7 @@ nwrite_dfns = 5000 n_rk_stages = 4 split_operators = false -#[numerical_dissipation] +#[ion_numerical_dissipation] #vpa_dissipation_coefficient = 0.0 #vperp_dissipation_coefficient = 0.0 #z_dissipation_coefficient = 0.1 diff --git a/util/compare_collision_frequencies.jl b/util/compare_collision_frequencies.jl index 8396e5cb7..a1fac065e 100644 --- a/util/compare_collision_frequencies.jl +++ b/util/compare_collision_frequencies.jl @@ -27,10 +27,10 @@ function compare_collision_frequencies(input_file::String, # v_∥ dissipation term is D d^2f/dv_∥^2. Inserting factors of c_ref, this is a bit like # pitch angle scattering D cref^2 d^2f/dv_∥^2 ~ D d^2f/dξ^2, so D is similar to a # (normalised) collision frequency. - if num_diss_params.vpa_dissipation_coefficient < 0.0 + if num_diss_params.ion.vpa_dissipation_coefficient < 0.0 nu_vpa_diss = 0.0 else - nu_vpa_diss = num_diss_params.vpa_dissipation_coefficient / + nu_vpa_diss = num_diss_params.ion.vpa_dissipation_coefficient / dimensional_parameters["timenorm"] end @@ -89,17 +89,17 @@ function compare_collision_frequencies(input_file::String, println("classical heat chi_i0 with effective rho_i ", effective_classical_heat_chi_i0) # Get numerical diffusion parameters - if num_diss_params.r_dissipation_coefficient < 0.0 + if num_diss_params.ion.r_dissipation_coefficient < 0.0 D_r = 0.0 else - D_r = Unitful.upreferred(num_diss_params.r_dissipation_coefficient * + D_r = Unitful.upreferred(num_diss_params.ion.r_dissipation_coefficient * dimensional_parameters["Lnorm"]^2 / dimensional_parameters["timenorm"]) end - if num_diss_params.z_dissipation_coefficient < 0.0 + if num_diss_params.ion.z_dissipation_coefficient < 0.0 D_z = 0.0 else - D_z = Unitful.upreferred(num_diss_params.z_dissipation_coefficient * + D_z = Unitful.upreferred(num_diss_params.ion.z_dissipation_coefficient * dimensional_parameters["Lnorm"]^2 / dimensional_parameters["timenorm"]) end