From 56295a490270686ca0ab65825c888836bb3ecbae Mon Sep 17 00:00:00 2001 From: John Omotani Date: Fri, 15 Mar 2024 17:46:43 +0000 Subject: [PATCH 01/42] Filter out size-1 dimensions from combinations to check in debug tests If dimension has size 1 for some input it cannot be parallelised, so there is no need to check it (for that case). By skipping size 1 dimensions, we can reduce the number of combinations that need debug tests, which speeds up the tests. --- .../debug_test/runtest_template.jl | 34 ++++++++++++++++++- .../debug_test/sound_wave_inputs.jl | 20 +++++------ 2 files changed, 43 insertions(+), 11 deletions(-) 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, From 32f94d6e6b5b6f342a5670cf72dfcf408739ee48 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Fri, 26 Apr 2024 14:10:48 +0100 Subject: [PATCH 02/42] Rename 'charged' to 'ion' --- .../src/makie_post_processing.jl | 18 +- moment_kinetics/debug_test/README.md | 2 +- moment_kinetics/ext/manufactured_solns_ext.jl | 28 +-- moment_kinetics/src/analysis.jl | 6 +- moment_kinetics/src/charge_exchange.jl | 20 +- moment_kinetics/src/continuity.jl | 10 +- moment_kinetics/src/derivatives.jl | 16 +- moment_kinetics/src/energy_equation.jl | 11 +- moment_kinetics/src/external_sources.jl | 40 ++-- moment_kinetics/src/file_io.jl | 113 +++++----- moment_kinetics/src/force_balance.jl | 10 +- moment_kinetics/src/initial_conditions.jl | 159 ++++++++------- moment_kinetics/src/ionization.jl | 4 +- moment_kinetics/src/krook_collisions.jl | 10 +- moment_kinetics/src/load_data.jl | 193 +++++++++--------- moment_kinetics/src/moment_kinetics.jl | 2 +- moment_kinetics/src/moment_kinetics_input.jl | 120 +++++------ .../src/moment_kinetics_structs.jl | 6 +- moment_kinetics/src/r_advection.jl | 6 +- moment_kinetics/src/source_terms.jl | 20 +- moment_kinetics/src/time_advance.jl | 135 ++++++------ moment_kinetics/src/velocity_moments.jl | 118 ++++++----- moment_kinetics/src/vpa_advection.jl | 28 +-- moment_kinetics/src/z_advection.jl | 6 +- .../test/Krook_collisions_tests.jl | 146 ++++++------- .../fokker_planck_time_evolution_tests.jl | 96 ++++----- ...ear_sound_wave_inputs_and_expected_data.jl | 56 ++--- .../test/nonlinear_sound_wave_tests.jl | 98 ++++----- .../test/restart_interpolation_tests.jl | 74 +++---- .../src/plot_MMS_sequence.jl | 12 +- .../src/plot_sequence.jl | 2 +- .../src/plots_post_processing.jl | 109 +++++----- 32 files changed, 839 insertions(+), 835 deletions(-) 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..c6395234c 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 @@ -303,7 +303,7 @@ 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 @@ -4302,9 +4302,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 +4318,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 +4332,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 @@ -5772,7 +5772,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 +5922,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 +5943,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 +6473,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) 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/ext/manufactured_solns_ext.jl b/moment_kinetics/ext/manufactured_solns_ext.jl index 2bad8dcf0..b2b9f471f 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 diff --git a/moment_kinetics/src/analysis.jl b/moment_kinetics/src/analysis.jl index 7daddfd17..e873b3d5b 100644 --- a/moment_kinetics/src/analysis.jl +++ b/moment_kinetics/src/analysis.jl @@ -13,7 +13,7 @@ using ..coordinates: coordinate using ..initial_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 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/continuity.jl b/moment_kinetics/src/continuity.jl index 80d0183c9..1c3600f07 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] @@ -42,7 +42,7 @@ function continuity_equation!(dens_out, fvec_in, moments, composition, dt, spect diffusion_coefficient = num_diss_params.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 diff --git a/moment_kinetics/src/derivatives.jl b/moment_kinetics/src/derivatives.jl index beeb45e66..e7b8b3994 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] """ @@ -87,7 +87,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}, dfdr_lower_endpoints::AbstractArray{mk_float,4}, dfdr_upper_endpoints::AbstractArray{mk_float,4}, @@ -139,7 +139,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] """ @@ -206,7 +206,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 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}, @@ -258,7 +258,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] """ @@ -335,7 +335,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}, @@ -398,7 +398,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] """ @@ -472,7 +472,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}, diff --git a/moment_kinetics/src/energy_equation.jl b/moment_kinetics/src/energy_equation.jl index efafe09de..8bc058dc1 100644 --- a/moment_kinetics/src/energy_equation.jl +++ b/moment_kinetics/src/energy_equation.jl @@ -17,13 +17,14 @@ 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 @@ -32,7 +33,7 @@ function energy_equation!(ppar, fvec, moments, collisions, dt, spectral, composi diffusion_coefficient = num_diss_params.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 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..debd37186 100644 --- a/moment_kinetics/src/file_io.jl +++ b/moment_kinetics/src/file_io.jl @@ -42,7 +42,7 @@ struct ascii_ios{T <: Union{IOStream,Nothing}} #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 @@ -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 @@ -130,7 +130,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 @@ -198,10 +198,10 @@ function setup_file_io(io_input, boundary_distributions, vz, vr, vzeta, vpa, vpe 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") + 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(mom_ion_io, mom_ntrl_io, fields_io) else ascii = ascii_ios(nothing, nothing, nothing) end @@ -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, @@ -657,49 +657,49 @@ function define_dynamic_moment_variables!(fid, n_ion_species, n_neutral_species, 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", + 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="charged species parallel flow", + 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="charged species parallel pressure", + 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="charged species perpendicular pressure", + 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="charged species parallel heat flux", + 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="charged species thermal speed", + 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="charged species entropy production", + description="ion species entropy production", units="") if parallel_io || z.irank == 0 @@ -951,7 +951,7 @@ function define_dynamic_dfn_variables!(fid, r, z, vperp, vpa, vzeta, vr, vz, io_f = create_dynamic_variable!(dynamic, "f", mk_float, vpa, vperp, z, r; n_ion_species=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; @@ -1256,66 +1256,66 @@ function write_moments_data_to_binary(moments, fields, t, n_ion_species, append_to_dynamic_var(io_moments.Ez, fields.Ez, t_idx, parallel_io, z, r) # 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 @@ -1457,7 +1457,7 @@ include("file_io_hdf5.jl") """ function write_data_to_ascii(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 +1465,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 +1483,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' @@ -1704,9 +1705,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/force_balance.jl b/moment_kinetics/src/force_balance.jl index 562f59972..3040be002 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] @@ -41,7 +41,7 @@ function force_balance!(pflx, density_out, fvec, moments, fields, collisions, dt diffusion_coefficient = num_diss_params.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 diff --git a/moment_kinetics/src/initial_conditions.jl b/moment_kinetics/src/initial_conditions.jl index a0bf9151f..f39524f00 100644 --- a/moment_kinetics/src/initial_conditions.jl +++ b/moment_kinetics/src/initial_conditions.jl @@ -27,8 +27,8 @@ using ..moment_kinetics_structs: scratch_pdf 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: moments_ion_substruct, moments_neutral_substruct 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! @@ -46,14 +46,14 @@ end # struct of structs neatly contains i+n info? struct pdf_struct - #charged particles: s + r + z + vperp + vpa - charged::pdf_substruct{5} + #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 - charged::moments_charged_substruct + ion::moments_ion_substruct neutral::moments_neutral_substruct # flag that indicates if the density should be evolved via continuity equation evolve_density::Bool @@ -72,8 +72,8 @@ 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} + # 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 @@ -91,7 +91,7 @@ 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) @@ -109,7 +109,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 +126,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 +155,42 @@ 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 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 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 +199,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 +230,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 +250,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 +537,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 +571,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 +631,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 +673,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) @@ -804,7 +812,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) @@ -929,31 +937,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() @@ -1011,7 +1018,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 @@ -1081,7 +1088,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 @@ -1089,8 +1096,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 @@ -1099,7 +1106,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 """ @@ -1116,17 +1123,17 @@ 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 @@ -1231,7 +1238,7 @@ function enforce_r_boundary_condition!(f::AbstractArray{mk_float,5}, f_r_bc, bc: end """ -enforce boundary conditions on charged particle f in z +enforce boundary conditions on ion 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}, @@ -1305,7 +1312,7 @@ end """ enforce boundary conditions on neutral particle distribution function """ -function enforce_neutral_boundary_conditions!(f_neutral, f_charged, +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, @@ -2023,7 +2030,7 @@ function vpagrid_to_dzdt(vpagrid, vth, upar, evolve_ppar, evolve_upar) 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. @@ -2032,7 +2039,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 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..2ef1997e4 100644 --- a/moment_kinetics/src/krook_collisions.jl +++ b/moment_kinetics/src/krook_collisions.jl @@ -98,7 +98,7 @@ 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] + vth = moments.ion.vth[iz,ir,is] nu_ii = get_collision_frequency(collisions, n, vth) @loop_vperp_vpa ivperp ivpa begin pdf_out[ivpa,ivperp,iz,ir,is] -= dt * nu_ii * @@ -111,7 +111,7 @@ 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] + vth = moments.ion.vth[iz,ir,is] nu_ii = get_collision_frequency(collisions, n, vth) @loop_vperp_vpa ivperp ivpa begin pdf_out[ivpa,ivperp,iz,ir,is] -= dt * nu_ii * @@ -124,7 +124,7 @@ 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] + vth = moments.ion.vth[iz,ir,is] nu_ii = get_collision_frequency(collisions, n, vth) @loop_vperp_vpa ivperp ivpa begin pdf_out[ivpa,ivperp,iz,ir,is] -= dt * nu_ii * @@ -138,7 +138,7 @@ 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] + vth = moments.ion.vth[iz,ir,is] nu_ii = get_collision_frequency(collisions, n, vth) @loop_vperp_vpa ivperp ivpa begin pdf_out[ivpa,ivperp,iz,ir,is] -= dt * nu_ii * @@ -151,7 +151,7 @@ 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 diff --git a/moment_kinetics/src/load_data.jl b/moment_kinetics/src/load_data.jl index 2ce2a5546..6b4740924 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 @@ -441,33 +441,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 @@ -514,12 +514,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 @@ -718,42 +718,42 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, 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") + moments.ion.dens .= load_moment("density") + moments.ion.dens_updated .= true + moments.ion.upar .= load_moment("parallel_flow") + moments.ion.upar_updated .= true + moments.ion.ppar .= load_moment("parallel_pressure") + moments.ion.ppar_updated .= true + moments.ion.qpar .= load_moment("parallel_heat_flux") + moments.ion.qpar_updated .= true + moments.ion.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 .= + elseif length(moments.ion.external_source_controller_integral) > 1 + moments.ion.external_source_controller_integral .= load_moment("external_source_controller_integral") end - function load_charged_pdf() + function load_ion_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) @@ -819,7 +819,7 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, # => 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] + 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, @@ -832,7 +832,7 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, # => 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] + 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, @@ -846,8 +846,8 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, 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] + @. (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, @@ -860,7 +860,7 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, # => 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] + 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, @@ -874,8 +874,8 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, 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 + @. (vpa.grid + moments.ion.upar[iz,ir,is]) / + moments.ion.vth @views interpolate_to_grid_1d!( new_pdf[:,ivperp,iz,ir,is], restart_vpa_vals, this_pdf[:,ivperp,iz,ir,is], restart_vpa, @@ -888,7 +888,7 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, # => 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] + 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, @@ -901,7 +901,7 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, # => 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] + 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, @@ -914,8 +914,8 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, # => 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] + 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, @@ -930,7 +930,7 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, 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] + 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, @@ -943,8 +943,8 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, # => 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] + 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, @@ -957,7 +957,7 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, # => 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] + 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, @@ -972,7 +972,7 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, 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] + 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, @@ -993,33 +993,33 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, 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] + 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.charged.dens[iz,ir,is] + 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.charged.vth[iz,ir,is] + 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.charged.vth[iz,ir,is] + this_pdf[:,:,iz,ir,is] ./= moments.ion.vth[iz,ir,is] end end return this_pdf end - pdf.charged.norm .= load_charged_pdf() + pdf.ion.norm .= load_ion_pdf() boundary_distributions_io = get_group(fid, "boundary_distributions") - function load_charged_boundary_pdf(var_name, ir) + function load_ion_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) @@ -1074,7 +1074,7 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, # => 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] + 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) @@ -1086,7 +1086,7 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, # => 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] + 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) @@ -1099,8 +1099,8 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, 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] + @. (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) @@ -1112,7 +1112,7 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, # => 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] + 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) @@ -1125,8 +1125,8 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, 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 + @. (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) @@ -1138,7 +1138,7 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, # => 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] + 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) @@ -1150,7 +1150,7 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, # => 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] + 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) @@ -1162,8 +1162,8 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, # => 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] + 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) @@ -1177,7 +1177,7 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, 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] + 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) @@ -1189,8 +1189,8 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, # => 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] + 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) @@ -1202,7 +1202,7 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, # => 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] + 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) @@ -1216,7 +1216,7 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, 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] + 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) @@ -1236,33 +1236,33 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, 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] + 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.charged.dens[iz,ir,is] + 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.charged.vth[iz,ir,is] + 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.charged.vth[iz,ir,is] + this_pdf[:,:,iz,is] ./= moments.ion.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,:] .= + load_ion_boundary_pdf("pdf_rboundary_ion_left", 1) + boundary_distributions.pdf_rboundary_ion[:,:,:,2,:] .= + load_ion_boundary_pdf("pdf_rboundary_ion_right", r.n) if composition.n_neutral_species > 0 moments.neutral.dens .= load_moment("density_neutral") @@ -1888,11 +1888,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[] @@ -2913,14 +2913,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, @@ -3145,12 +3144,12 @@ 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]), evolve_density=run_info.evolve_density, evolve_upar=run_info.evolve_upar, evolve_ppar=run_info.evolve_ppar) diff --git a/moment_kinetics/src/moment_kinetics.jl b/moment_kinetics/src/moment_kinetics.jl index 5076e14ef..0d83b5219 100644 --- a/moment_kinetics/src/moment_kinetics.jl +++ b/moment_kinetics/src/moment_kinetics.jl @@ -427,7 +427,7 @@ function setup_moment_kinetics(input_dict::AbstractDict; 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, + write_dfns_data_to_binary(pdf.ion.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) diff --git a/moment_kinetics/src/moment_kinetics_input.jl b/moment_kinetics/src/moment_kinetics_input.jl index 153d602b8..66b24800c 100644 --- a/moment_kinetics/src/moment_kinetics_input.jl +++ b/moment_kinetics/src/moment_kinetics_input.jl @@ -121,39 +121,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 +169,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 +179,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 +192,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 +336,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 +354,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 +398,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 +415,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") @@ -518,32 +518,32 @@ 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) - 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) - 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) - species_charged_immutable[is] = species_parameters(species_type, species.charged[is].initial_temperature, - species.charged[is].initial_density, z_IC, r_IC, vpa_IC) + 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) + 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) + 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) + 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 @@ -570,7 +570,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) @@ -1005,7 +1005,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ₑ @@ -1076,7 +1076,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 @@ -1088,7 +1088,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..6efe78dcd 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} 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/source_terms.jl b/moment_kinetics/src/source_terms.jl index 53dd88b26..790603f34 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,7 +65,7 @@ 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_amplitude = moments.ion.external_source_amplitude @loop_r_z ir iz begin term = dt * source_amplitude[iz,ir] / dens[iz,ir] @loop_vperp_vpa ivperp ivpa begin @@ -97,7 +97,7 @@ 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_amplitude = moments.ion.external_source_amplitude source_T = ion_source_settings.source_T @loop_r_z ir iz begin term = dt * source_amplitude[iz,ir] * @@ -257,7 +257,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 +267,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 +288,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 f7f1e1c06..629fb2c3c 100644 --- a/moment_kinetics/src/time_advance.jl +++ b/moment_kinetics/src/time_advance.jl @@ -20,7 +20,7 @@ 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! @@ -301,7 +301,7 @@ 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, @@ -331,12 +331,12 @@ 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, + calculate_ion_moment_derivatives!(moments, scratch[1], scratch_dummy, z, z_spectral, num_diss_params) + calculate_neutral_moment_derivatives!(moments, scratch[1], scratch_dummy, z, z_spectral, num_diss_params) ## - # Charged particle advection only + # ion particle advection only ## # create structure r_advect whose members are the arrays needed to compute @@ -348,8 +348,8 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz # 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 @@ -364,8 +364,8 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz # 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 @@ -468,13 +468,13 @@ 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!(pdf.ion.norm, num_diss_params) force_minimum_pdf_value_neutral!(pdf.neutral.norm, num_diss_params) # 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, @@ -482,22 +482,22 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz # 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, + @views hard_force_moment_constraints!(pdf.ion.norm[:,:,iz,ir,is], moments, vpa) 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) @@ -514,10 +514,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) @@ -531,8 +531,8 @@ 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, + calculate_ion_moment_derivatives!(moments, scratch[1], scratch_dummy, z, z_spectral, num_diss_params) + calculate_neutral_moment_derivatives!(moments, scratch[1], scratch_dummy, z, z_spectral, num_diss_params) # Ensure all processes are synchronized at the end of the setup @@ -832,12 +832,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 @@ -862,11 +862,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 @@ -1143,7 +1143,7 @@ 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, + write_dfns_data_to_binary(pdf.ion.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, @@ -1189,7 +1189,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, @@ -1197,7 +1197,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, @@ -1326,7 +1326,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, @@ -1334,7 +1334,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, @@ -1385,7 +1385,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 @@ -1406,7 +1406,7 @@ 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) @@ -1427,7 +1427,7 @@ 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, + calculate_ion_moment_derivatives!(moments, new_scratch, scratch_dummy, z, z_spectral, num_diss_params) end update_derived_ion_moments_and_derivatives() @@ -1487,7 +1487,7 @@ 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, + calculate_neutral_moment_derivatives!(moments, new_scratch, scratch_dummy, z, z_spectral, num_diss_params) end update_derived_neutral_moments_and_derivatives() @@ -1586,9 +1586,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) @@ -1751,7 +1750,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) @@ -1765,20 +1764,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 @@ -1789,7 +1788,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") @@ -1802,8 +1801,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 @@ -1846,13 +1845,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 @@ -1898,13 +1897,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 @@ -1977,7 +1976,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 @@ -2000,7 +1999,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, @@ -2050,9 +2049,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 @@ -2063,7 +2062,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 @@ -2072,7 +2071,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 @@ -2115,7 +2114,7 @@ function euler_time_advance!(fvec_out, fvec_in, pdf, fields, moments, # advance with the Fokker-Planck self-collision operator if advance.explicit_weakform_fp_collisions update_entropy_diagnostic = (istage == 1) - explicit_fokker_planck_collisions_weak_form!(fvec_out.pdf,fvec_in.pdf,moments.charged.dSdt,composition,collisions,dt, + 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 diff --git a/moment_kinetics/src/velocity_moments.jl b/moment_kinetics/src/velocity_moments.jl index 3da3e251f..0ac581b41 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,7 @@ export update_pperp! export update_qpar! export update_vth! export reset_moments_status! -export moments_chrg_substruct, moments_ntrl_substruct +export moments_ion_substruct, moments_neutral_substruct export update_neutral_density! export update_neutral_uz! export update_neutral_ur! @@ -49,7 +49,7 @@ using ..gyroaverages: gyro_operators, gyroaverage_pdf! """ """ -struct moments_charged_substruct +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 @@ -213,8 +213,8 @@ 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, numerical_dissipation) # allocate array used for the particle density density = allocate_shared_float(nz, nr, n_species) # allocate array of Bools that indicate if the density is updated for each species @@ -337,7 +337,7 @@ function create_moments_charged(nz, nr, n_species, evolve_density, evolve_upar, 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, @@ -492,7 +492,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 +500,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 +884,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 +947,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, + num_diss_params) 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 +964,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 && num_diss_params.moment_dissipation_coefficient > 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 +992,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 && num_diss_params.moment_dissipation_coefficient > 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 +1013,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,7 +1504,7 @@ 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, +function calculate_neutral_moment_derivatives!(moments, scratch, scratch_dummy, z, z_spectral, numerical_dissipation) begin_sn_r_region() @@ -1800,18 +1798,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..3faed5ba4 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,17 +145,17 @@ 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_amplitude = moments.ion.external_source_amplitude source_T = ion_source_settings.source_T 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] @@ -187,9 +187,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 +228,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,14 +253,14 @@ 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_amplitude = moments.ion.external_source_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] @loop_vperp_vpa ivperp ivpa begin 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..2e6a2dc5e 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;;; @@ -191,10 +191,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 +222,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 +232,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 +255,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 +263,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 +281,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 +293,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 +305,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 +317,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 +334,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/fokker_planck_time_evolution_tests.jl b/moment_kinetics/test/fokker_planck_time_evolution_tests.jl index 2d251082b..d93d427ee 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 @@ -211,14 +211,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 +243,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 +255,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 +288,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/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..f6e5d7334 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 @@ -58,10 +58,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 +89,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 +99,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 +122,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 +130,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 +148,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 +160,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 +172,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 +184,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 +201,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/restart_interpolation_tests.jl b/moment_kinetics/test/restart_interpolation_tests.jl index 7d7ceb14e..c4d19e288 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 @@ -120,10 +120,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 @@ -155,12 +155,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") @@ -181,12 +181,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,:,:] @@ -197,7 +197,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] @@ -205,7 +205,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] @@ -216,36 +216,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 ###################################### 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 468f56319..53b003cac 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) @@ -2953,10 +2952,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) @@ -3135,14 +3134,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 @@ -3408,9 +3407,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) @@ -3422,10 +3421,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 @@ -3455,7 +3454,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. @@ -3467,7 +3466,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. From 4908febfb9b3251dedf09057453d53aa7b526346 Mon Sep 17 00:00:00 2001 From: Lucas McConnell <89389250+LucasMontoya4@users.noreply.github.com> Date: Thu, 28 Mar 2024 11:55:48 +0100 Subject: [PATCH 03/42] Separate dissipation parameters for ions, electrons, neutrals (#192) --- .gitignore | 1 + ...lanck-1D2V-even_nz-shorttest-nstep200.toml | 2 +- examples/geometry/1D-mirror.toml | 2 +- examples/gk-ions/2D-periodic-gk.toml | 16 +- .../num-diss-relaxation.toml | 2 +- ...all-bc_recyclefraction0.5_split3-init.toml | 8 +- .../wall-bc_recyclefraction0.5_split3.toml | 8 +- ...c_recyclefraction0.5_split3_fekete104.toml | 8 +- ...bc_recyclefraction0.5_split3_fekete43.toml | 8 +- ...bc_recyclefraction0.5_split3_fekete64.toml | 6 +- examples/wall-bc/wall-bc_cheb.toml | 6 +- examples/wall-bc/wall-bc_cheb_split1.toml | 6 +- examples/wall-bc/wall-bc_cheb_split2.toml | 6 +- examples/wall-bc/wall-bc_cheb_split3.toml | 6 +- examples/wall-bc/wall-bc_no-neutrals.toml | 2 +- .../wall-bc/wall-bc_no-neutrals_split1.toml | 2 +- .../wall-bc/wall-bc_no-neutrals_split2.toml | 2 +- .../wall-bc/wall-bc_no-neutrals_split3.toml | 2 +- examples/wall-bc/wall-bc_volumerecycle.toml | 8 +- .../wall-bc/wall-bc_volumerecycle_split1.toml | 8 +- .../wall-bc/wall-bc_volumerecycle_split2.toml | 8 +- .../wall-bc/wall-bc_volumerecycle_split3.toml | 8 +- .../restart_interpolation_inputs.jl | 3 +- moment_kinetics/ext/manufactured_solns_ext.jl | 28 ++-- moment_kinetics/src/continuity.jl | 4 +- moment_kinetics/src/energy_equation.jl | 4 +- moment_kinetics/src/force_balance.jl | 4 +- moment_kinetics/src/initial_conditions.jl | 6 +- moment_kinetics/src/moment_kinetics_input.jl | 13 +- moment_kinetics/src/numerical_dissipation.jl | 140 ++++++++++++------ moment_kinetics/src/time_advance.jl | 51 ++++--- moment_kinetics/src/velocity_moments.jl | 30 ++-- moment_kinetics/test/harrisonthompson.jl | 2 +- .../test/recycling_fraction_tests.jl | 3 +- .../wall-bc/wall-bc_recyclefraction0.5.toml | 8 +- .../wall-bc_recyclefraction0.5_split1.toml | 8 +- .../wall-bc_recyclefraction0.5_split2.toml | 8 +- .../wall-bc_recyclefraction0.5_split3.toml | 8 +- ..._new_nel_r_1_z_16_vpa_16_vperp_8_diss.toml | 2 +- ...MS_new_nel_r_1_z_6_vpa_6_vperp_3_diss.toml | 2 +- ...id_9_nel_r_1_z_16_vpa_16_vperp_8_diss.toml | 2 +- ...d_9_nel_r_1_z_32_vpa_32_vperp_16_diss.toml | 2 +- ...grid_9_nel_r_1_z_4_vpa_4_vperp_2_diss.toml | 2 +- ...d_9_nel_r_1_z_64_vpa_64_vperp_32_diss.toml | 2 +- ...grid_9_nel_r_1_z_8_vpa_8_vperp_4_diss.toml | 2 +- ..._new_nel_r_1_z_16_vpa_16_vperp_1_diss.toml | 2 +- ...new_nel_r_1_z_16_vpa_16_vperp_1_krook.toml | 2 +- ..._new_nel_r_1_z_16_vpa_8_vperp_8_krook.toml | 2 +- ...d_5_nel_r_16_z_16_vpa_16_vperp_8_diss.toml | 2 +- ..._5_nel_r_32_z_32_vpa_32_vperp_16_diss.toml | 2 +- ...grid_5_nel_r_4_z_4_vpa_4_vperp_2_diss.toml | 2 +- ...grid_5_nel_r_8_z_8_vpa_8_vperp_4_diss.toml | 2 +- ...MMS_nel_r_16_z_16_vpa_16_vperp_1_diss.toml | 2 +- ...l_MMS_nel_r_2_z_2_vpa_16_vperp_1_diss.toml | 2 +- ...MMS_nel_r_32_z_32_vpa_16_vperp_1_diss.toml | 2 +- ...MS_nel_r_32_z_32_vpa_16_vperp_1_diss5.toml | 2 +- ...l_MMS_nel_r_4_z_4_vpa_16_vperp_1_diss.toml | 2 +- ...l_MMS_nel_r_8_z_8_vpa_16_vperp_1_diss.toml | 2 +- ...3_nel_r_1_z_1_vpa_6_vperp_3_fkpl_test.toml | 2 +- util/compare_collision_frequencies.jl | 12 +- 60 files changed, 322 insertions(+), 177 deletions(-) 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/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/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/ext/manufactured_solns_ext.jl b/moment_kinetics/ext/manufactured_solns_ext.jl index b2b9f471f..cda63a819 100644 --- a/moment_kinetics/ext/manufactured_solns_ext.jl +++ b/moment_kinetics/ext/manufactured_solns_ext.jl @@ -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/continuity.jl b/moment_kinetics/src/continuity.jl index 1c3600f07..26e9e1314 100644 --- a/moment_kinetics/src/continuity.jl +++ b/moment_kinetics/src/continuity.jl @@ -39,7 +39,7 @@ 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.ion.d2dens_dz2[iz,ir,is] @@ -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/energy_equation.jl b/moment_kinetics/src/energy_equation.jl index 8bc058dc1..8890be3b6 100644 --- a/moment_kinetics/src/energy_equation.jl +++ b/moment_kinetics/src/energy_equation.jl @@ -30,7 +30,7 @@ function energy_equation!(ppar, fvec, moments, collisions, dt, spectral, composi 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.ion.d2ppar_dz2[iz,ir,is] @@ -82,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/force_balance.jl b/moment_kinetics/src/force_balance.jl index 3040be002..902c3901e 100644 --- a/moment_kinetics/src/force_balance.jl +++ b/moment_kinetics/src/force_balance.jl @@ -38,7 +38,7 @@ 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.ion.d2upar_dz2[iz,ir,is]*density[iz,ir,is] @@ -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 f39524f00..e5c517e9c 100644 --- a/moment_kinetics/src/initial_conditions.jl +++ b/moment_kinetics/src/initial_conditions.jl @@ -83,7 +83,7 @@ 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 @@ -94,11 +94,11 @@ function allocate_pdf_and_moments(composition, r, z, vperp, vpa, vzeta, vr, vz, 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 diff --git a/moment_kinetics/src/moment_kinetics_input.jl b/moment_kinetics/src/moment_kinetics_input.jl index 66b24800c..e28d118d9 100644 --- a/moment_kinetics/src/moment_kinetics_input.jl +++ b/moment_kinetics/src/moment_kinetics_input.jl @@ -427,14 +427,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") ######################################################################### 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/time_advance.jl b/moment_kinetics/src/time_advance.jl index 629fb2c3c..2020ca6cc 100644 --- a/moment_kinetics/src/time_advance.jl +++ b/moment_kinetics/src/time_advance.jl @@ -195,6 +195,8 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz n_species = composition.n_species 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 + neutral_mom_diss_coeff = num_diss_params.neutral.moment_dissipation_coefficient dt_shared = allocate_shared_float(1) previous_dt_shared = allocate_shared_float(1) @@ -331,9 +333,10 @@ 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_ion_moment_derivatives!(moments, scratch[1], scratch_dummy, z, z_spectral, num_diss_params) - calculate_neutral_moment_derivatives!(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) ## # ion particle advection only @@ -468,8 +471,8 @@ 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.ion.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!( @@ -531,9 +534,10 @@ 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_ion_moment_derivatives!(moments, scratch[1], scratch_dummy, z, z_spectral, num_diss_params) - calculate_neutral_moment_derivatives!(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() @@ -681,11 +685,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 @@ -1396,7 +1400,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 @@ -1428,7 +1432,7 @@ function rk_update!(scratch, pdf, moments, fields, boundary_distributions, vz, v r_spectral, geometry, gyroavs, scratch_dummy, z_advect, diagnostic_moments) calculate_ion_moment_derivatives!(moments, new_scratch, scratch_dummy, z, z_spectral, - num_diss_params) + num_diss_params.ion.moment_dissipation_coefficient) end update_derived_ion_moments_and_derivatives() @@ -1443,7 +1447,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 @@ -1488,7 +1492,8 @@ function rk_update!(scratch, pdf, moments, fields, boundary_distributions, vz, v moments.evolve_ppar) calculate_neutral_moment_derivatives!(moments, new_scratch, scratch_dummy, z, - z_spectral, num_diss_params) + z_spectral, + num_diss_params.neutral.moment_dissipation_coefficient) end update_derived_neutral_moments_and_derivatives() end @@ -2097,19 +2102,19 @@ 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 diff --git a/moment_kinetics/src/velocity_moments.jl b/moment_kinetics/src/velocity_moments.jl index 0ac581b41..87c429395 100644 --- a/moment_kinetics/src/velocity_moments.jl +++ b/moment_kinetics/src/velocity_moments.jl @@ -214,7 +214,7 @@ end """ """ function create_moments_ion(nz, nr, n_species, evolve_density, evolve_upar, - evolve_ppar, ion_source_settings, numerical_dissipation) + 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 +258,7 @@ function create_moments_ion(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 +274,7 @@ function create_moments_ion(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 @@ -355,7 +353,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 +396,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 +412,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 @@ -948,7 +944,7 @@ end Pre-calculate spatial derivatives of the moments that will be needed for the time advance """ function calculate_ion_moment_derivatives!(moments, scratch, scratch_dummy, z, z_spectral, - num_diss_params) + ion_mom_diss_coeff) begin_s_r_region() density = scratch.density @@ -974,7 +970,7 @@ function calculate_ion_moment_derivatives!(moments, scratch, scratch_dummy, z, z 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 && num_diss_params.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, @@ -996,7 +992,7 @@ function calculate_ion_moment_derivatives!(moments, scratch, scratch_dummy, z, z 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 && num_diss_params.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, @@ -1505,7 +1501,7 @@ Pre-calculate spatial derivatives of the neutral moments that will be needed for advance """ function calculate_neutral_moment_derivatives!(moments, scratch, scratch_dummy, z, - z_spectral, numerical_dissipation) + z_spectral, neutral_mom_diss_coeff) begin_sn_r_region() density = scratch.density_neutral @@ -1532,8 +1528,7 @@ function calculate_neutral_moment_derivatives!(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, @@ -1557,8 +1552,7 @@ function calculate_neutral_moment_derivatives!(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, diff --git a/moment_kinetics/test/harrisonthompson.jl b/moment_kinetics/test/harrisonthompson.jl index 8ad05dd24..5f9a05164 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", diff --git a/moment_kinetics/test/recycling_fraction_tests.jl b/moment_kinetics/test/recycling_fraction_tests.jl index d4b833817..aaca8d80b 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)) 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 From 9a312416e7fd2d075c0148a52044a2b52a0b635e Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sun, 21 Apr 2024 20:56:53 +0100 Subject: [PATCH 04/42] Fix duplicated calculations in steady state residual check --- moment_kinetics/src/analysis.jl | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/moment_kinetics/src/analysis.jl b/moment_kinetics/src/analysis.jl index e873b3d5b..a349cdb10 100644 --- a/moment_kinetics/src/analysis.jl +++ b/moment_kinetics/src/analysis.jl @@ -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) From 69271f5d2d068a0951bcbdcff8169b2283deab58 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sun, 21 Apr 2024 14:40:55 +0100 Subject: [PATCH 05/42] Try-catch in calculate_steady_state_residual() post-proc function --- .../src/makie_post_processing.jl | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) 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 c6395234c..b17842071 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 @@ -3524,24 +3524,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, From 9f22caf9be6f24131c4cbd2178a88e2d0b9410b0 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Fri, 26 Apr 2024 16:45:08 +0100 Subject: [PATCH 06/42] Throw error when assign_endpoint!() fails --- moment_kinetics/src/calculus.jl | 68 ++++++++++++++++----------------- 1 file changed, 34 insertions(+), 34 deletions(-) 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 From f20cb833db2261f7b8bef035d92315bb427f4e15 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Fri, 26 Apr 2024 17:24:39 +0100 Subject: [PATCH 07/42] Move boundary conditions out of initial_conditions to separate module --- moment_kinetics/src/analysis.jl | 2 +- moment_kinetics/src/boundary_conditions.jl | 1006 ++++++++++++++++++++ moment_kinetics/src/initial_conditions.jl | 983 +------------------ moment_kinetics/src/moment_constraints.jl | 1 - moment_kinetics/src/moment_kinetics.jl | 8 +- moment_kinetics/src/time_advance.jl | 4 +- 6 files changed, 1014 insertions(+), 990 deletions(-) create mode 100644 moment_kinetics/src/boundary_conditions.jl diff --git a/moment_kinetics/src/analysis.jl b/moment_kinetics/src/analysis.jl index a349cdb10..fc342e050 100644 --- a/moment_kinetics/src/analysis.jl +++ b/moment_kinetics/src/analysis.jl @@ -10,7 +10,7 @@ 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_ion_pdf_slice 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/initial_conditions.jl b/moment_kinetics/src/initial_conditions.jl index e5c517e9c..da118dbad 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,15 +15,13 @@ 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 ..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_ion, create_moments_neutral, update_qpar! using ..velocity_moments: moments_ion_substruct, moments_neutral_substruct @@ -1138,896 +1134,6 @@ function init_boundary_distributions!(boundary_distributions, 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 ion 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_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() - @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 ion distribution function, calculate the moments, then @@ -2128,91 +1234,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/moment_constraints.jl b/moment_kinetics/src/moment_constraints.jl index fbdf384a1..eba1bc12a 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! diff --git a/moment_kinetics/src/moment_kinetics.jl b/moment_kinetics/src/moment_kinetics.jl index 0d83b5219..bab09f5e2 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 @@ -83,8 +84,7 @@ 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! diff --git a/moment_kinetics/src/time_advance.jl b/moment_kinetics/src/time_advance.jl index 2020ca6cc..42105a24e 100644 --- a/moment_kinetics/src/time_advance.jl +++ b/moment_kinetics/src/time_advance.jl @@ -23,8 +23,8 @@ using ..velocity_moments: update_neutral_pzeta!, update_neutral_pz!, update_neut 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 ..boundary_conditions: enforce_boundary_conditions! +using ..boundary_conditions: enforce_neutral_boundary_conditions! using ..input_structs: advance_info, time_info using ..moment_constraints: hard_force_moment_constraints!, hard_force_moment_constraints_neutral! From 8208c548bc3b496ac5f05a0badb23ed07f247a90 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Wed, 7 Feb 2024 10:09:17 +0000 Subject: [PATCH 08/42] Include begin_*_region() calls in derivative functions Ensures that a correct region type is being used, rather than relying on the calling site of the derivative_*!() function to have already set a correct region type. --- moment_kinetics/src/derivatives.jl | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/moment_kinetics/src/derivatives.jl b/moment_kinetics/src/derivatives.jl index e7b8b3994..409274b0f 100644 --- a/moment_kinetics/src/derivatives.jl +++ b/moment_kinetics/src/derivatives.jl @@ -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) @@ -94,6 +96,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, 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) @@ -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) @@ -213,6 +221,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, 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) @@ -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) @@ -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], @@ -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) @@ -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], From 65675cd602cd56161b04c59861d56b054b073ca4 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Mon, 1 Apr 2024 10:21:37 +0100 Subject: [PATCH 09/42] Set debug flags in ldiv!() method for MPIDebugSharedArray --- moment_kinetics/src/communication.jl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/moment_kinetics/src/communication.jl b/moment_kinetics/src/communication.jl index cd2bc53cd..4280ad4a8 100644 --- a/moment_kinetics/src/communication.jl +++ b/moment_kinetics/src/communication.jl @@ -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 From dc11be8d2c8667939a45e52981a84a8e3a74350a Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sun, 7 Apr 2024 09:40:31 +0100 Subject: [PATCH 10/42] Let value in setindex!() method for DebugMPISharedArray be any Number Allows `setindex!()` for DebugMPISharedArray to be used with, for example, `Float128` values, not just `mk_float`. Any type that is OK for the underlying `setindex!()` of `Array{mk_float,N}` should be supported. --- moment_kinetics/src/communication.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moment_kinetics/src/communication.jl b/moment_kinetics/src/communication.jl index 4280ad4a8..2550d0358 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 From c96a360c7064b30b97d21ba8355cd78b6f32a4d8 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 17 Feb 2024 13:40:26 +0000 Subject: [PATCH 11/42] Save run_id in io_input This makes it easier to add the run_id to multiple output files, which might not be created by the same function. --- moment_kinetics/src/file_io.jl | 11 +---------- moment_kinetics/src/input_structs.jl | 1 + moment_kinetics/src/moment_kinetics_input.jl | 10 ++++++++++ 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/moment_kinetics/src/file_io.jl b/moment_kinetics/src/file_io.jl index debd37186..509fe6dfb 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 @@ -206,7 +205,7 @@ function setup_file_io(io_input, boundary_distributions, vz, vr, vzeta, vpa, vpe ascii = ascii_ios(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 +305,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") diff --git a/moment_kinetics/src/input_structs.jl b/moment_kinetics/src/input_structs.jl index cc75a543d..2a9a6a875 100644 --- a/moment_kinetics/src/input_structs.jl +++ b/moment_kinetics/src/input_structs.jl @@ -399,6 +399,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/moment_kinetics_input.jl b/moment_kinetics/src/moment_kinetics_input.jl index e28d118d9..456779ec7 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 @@ -587,6 +588,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)...) From 5cf7b186d6bd9aeaf9daa67bab089cabdadc238d Mon Sep 17 00:00:00 2001 From: John Omotani Date: Fri, 16 Feb 2024 16:21:05 +0000 Subject: [PATCH 12/42] Split I/O for ions and neutrals into separate functions The separate functions are also called by a single output function so the original call-site does not get more complicated, but the individual functions can be called on their own if necessary. --- moment_kinetics/src/file_io.jl | 751 ++++++++++++++++--------- moment_kinetics/src/moment_kinetics.jl | 13 +- moment_kinetics/src/time_advance.jl | 19 +- 3 files changed, 504 insertions(+), 279 deletions(-) diff --git a/moment_kinetics/src/file_io.jl b/moment_kinetics/src/file_io.jl index 509fe6dfb..239fc1bc2 100644 --- a/moment_kinetics/src/file_io.jl +++ b/moment_kinetics/src/file_io.jl @@ -628,233 +628,320 @@ 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="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_phi, io_Er, io_Ez = + define_dynamic_em_field_variables!(fid, r, z, parallel_io, + external_source_settings) + + 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 = + 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 = + define_dynamic_neutral_moment_variables!(fid, n_neutral_species, r, z, + parallel_io, + external_source_settings, + evolve_density, evolve_upar, + evolve_ppar) - # io_dSdt is the handle for the entropy production (due to collisions) - io_dSdt = create_dynamic_variable!(dynamic, "entropy_production", mk_float, z, r; + io_time_for_run = create_dynamic_variable!( + dynamic, "time_for_run", mk_float; parallel_io=parallel_io, + description="cumulative wall clock time for run (excluding setup)", + units="minutes") + + 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_pz_neutral, io_qz_neutral, io_thermal_speed_neutral, + external_source_amplitude, + external_source_density_amplitude, + external_source_momentum_amplitude, + external_source_pressure_amplitude, + external_source_controller_integral, + external_source_neutral_amplitude, + external_source_neutral_density_amplitude, + external_source_neutral_momentum_amplitude, + external_source_neutral_pressure_amplitude, + external_source_neutral_controller_integral, + io_time_for_run, parallel_io) + end + + # For processes other than the root process of each shared-memory group... + 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 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") + 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 - io_chodura_lower = nothing + external_source_density_amplitude = 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") + 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 - io_chodura_upper = nothing + external_source_momentum_amplitude = 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 + 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 = nothing + 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_amplitude = nothing - external_source_density_amplitude = nothing - external_source_momentum_amplitude = nothing - external_source_pressure_amplitude = nothing 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 + 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 + + 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 +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_amplitude = nothing 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_time_for_run = create_dynamic_variable!( dynamic, "time_for_run", mk_float; parallel_io=parallel_io, @@ -1220,11 +1307,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' @@ -1241,11 +1328,68 @@ 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_or_file_info_moments, t_idx, r, z) + + write_ion_moments_data_to_binary(moments, n_ion_species, io_or_file_info_moments, + t_idx, r, z) + + write_neutral_moments_data_to_binary(moments, n_neutral_species, + io_or_file_info_moments, t_idx, r, z) + + append_to_dynamic_var(io_moments.time_for_run, time_for_run, t_idx, parallel_io) + + closefile && close(io_moments.fid) + end + + return nothing +end + +""" +write time-dependent EM fields data to the binary output file +""" +function write_em_fields_data_to_binary(fields, io_or_file_info_moments, t_idx, r, z) + @serial_region begin + # Only read/write from first process in each 'block' + + if isa(io_or_file_info_moments, io_moments_info) + io_moments = io_or_file_info_moments + closefile = false + else + io_moments = reopen_moments_io(io_or_file_info_moments) + closefile = true + end + + 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) + closefile && close(io_moments.fid) + end + + return nothing +end + +""" +write time-dependent moments data for ions to the binary output file +""" +function write_ion_moments_data_to_binary(moments, n_ion_species, io_or_file_info_moments, + t_idx, r, z) + @serial_region begin + # Only read/write from first process in each 'block' + + if isa(io_or_file_info_moments, io_moments_info) + io_moments = io_or_file_info_moments + closefile = false + else + io_moments = reopen_moments_io(io_or_file_info_moments) + closefile = true + end + + 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.ion.dens, t_idx, parallel_io, z, r, n_ion_species) @@ -1310,52 +1454,77 @@ function write_moments_data_to_binary(moments, fields, t, n_ion_species, 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, - 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 + + closefile && close(io_moments.fid) + end + + return nothing +end + +""" +write time-dependent moments data for neutrals to the binary output file +""" +function write_neutral_moments_data_to_binary(moments, n_neutral_species, + io_or_file_info_moments, t_idx, r, z) + if n_neutral_species ≤ 0 + return nothing + end + + @serial_region begin + # Only read/write from first process in each 'block' + + if isa(io_or_file_info_moments, io_moments_info) + io_moments = io_or_file_info_moments + closefile = false + else + io_moments = reopen_moments_io(io_or_file_info_moments) + closefile = true + end + + 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 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 + 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 - - 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) @@ -1370,16 +1539,18 @@ function write_moments_data_to_binary(moments, fields, t, n_ion_species, 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' @@ -1393,15 +1564,67 @@ 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_or_file_info_dfns, + t_idx, r, z, vperp, vpa) + write_neutral_dfns_data_to_binary(pdf.neutral.norm, n_neutral_species, + io_or_file_info_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 +""" +function write_ion_dfns_data_to_binary(ff, n_ion_species, io_or_file_info_dfns, t_idx, r, + z, vperp, vpa) + @serial_region begin + # Only read/write from first process in each 'block' + + if isa(io_or_file_info_dfns, io_dfns_info) + io_dfns = io_or_file_info_dfns + closefile = false + else + io_dfns = reopen_dfns_io(io_or_file_info_dfns) + closefile = true + end 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) + + closefile && close(io_dfns.fid) + end + return nothing +end + +""" +write time-dependent distribution function data for neutrals to the binary output file +""" +function write_neutral_dfns_data_to_binary(ff_neutral, n_neutral_species, + io_or_file_info_dfns, t_idx, r, z, vzeta, vr, + vz) + @serial_region begin + # Only read/write from first process in each 'block' + + if isa(io_or_file_info_dfns, io_dfns_info) + io_dfns = io_or_file_info_dfns + closefile = false + else + io_dfns = reopen_dfns_io(io_or_file_info_dfns) + closefile = true + end + + 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) diff --git a/moment_kinetics/src/moment_kinetics.jl b/moment_kinetics/src/moment_kinetics.jl index bab09f5e2..da4c3edb4 100644 --- a/moment_kinetics/src/moment_kinetics.jl +++ b/moment_kinetics/src/moment_kinetics.jl @@ -77,7 +77,7 @@ 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 @@ -425,11 +425,12 @@ function setup_moment_kinetics(input_dict::AbstractDict; 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.ion.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.ion.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) begin_s_r_z_vperp_region() diff --git a/moment_kinetics/src/time_advance.jl b/moment_kinetics/src/time_advance.jl index 42105a24e..ec76d4c28 100644 --- a/moment_kinetics/src/time_advance.jl +++ b/moment_kinetics/src/time_advance.jl @@ -12,7 +12,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! @@ -1065,9 +1065,10 @@ function time_advance!(pdf, scratch, t, t_params, vz, vr, vzeta, vpa, vperp, gyr write_data_to_ascii(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 @@ -1147,11 +1148,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.ion.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 From 8aeefda4d421a6f7a9a613c1edddb4d5ac0d33c4 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Mon, 11 Mar 2024 11:44:58 +0000 Subject: [PATCH 13/42] Write out most recent version of constraints coefficients as diagnostic It can be useful to know how much of a change the 'moment constraint corrections' are having to apply. To help diagnose this, store the values of the coefficients used to apply the constraints (for each species of ion and neutral, at each spatial point) into arrays in the `moments` structs, and write out the most recent values at each output. --- .../src/makie_post_processing.jl | 350 ++++++++++++++++++ moment_kinetics/src/file_io.jl | 325 +++++++++------- moment_kinetics/src/initial_conditions.jl | 10 + moment_kinetics/src/moment_constraints.jl | 20 +- moment_kinetics/src/moment_kinetics.jl | 6 +- moment_kinetics/src/time_advance.jl | 40 +- moment_kinetics/src/velocity_moments.jl | 44 ++- 7 files changed, 634 insertions(+), 161 deletions(-) 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 b17842071..9377504bb 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 @@ -308,6 +308,8 @@ function makie_post_process(run_dir::Union{String,Tuple}, 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"] @@ -684,6 +686,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, @@ -4586,6 +4604,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) diff --git a/moment_kinetics/src/file_io.jl b/moment_kinetics/src/file_io.jl index 239fc1bc2..21d2e6977 100644 --- a/moment_kinetics/src/file_io.jl +++ b/moment_kinetics/src/file_io.jl @@ -54,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 @@ -102,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 @@ -629,13 +638,14 @@ function define_dynamic_moment_variables!(fid, n_ion_species, n_neutral_species, description="simulation time") io_phi, io_Er, io_Ez = - define_dynamic_em_field_variables!(fid, r, z, parallel_io, - external_source_settings) + 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 = + 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) @@ -645,7 +655,8 @@ function define_dynamic_moment_variables!(fid, n_ion_species, n_neutral_species, external_source_neutral_density_amplitude, external_source_neutral_momentum_amplitude, external_source_neutral_pressure_amplitude, - external_source_neutral_controller_integral = + 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, @@ -657,6 +668,42 @@ function define_dynamic_moment_variables!(fid, n_ion_species, n_neutral_species, description="cumulative wall clock time for run (excluding setup)", units="minutes") + io_step_counter = create_dynamic_variable!( + dynamic, "step_counter", mk_int; parallel_io=parallel_io, + description="cumulative number of timesteps for the run") + + io_dt = create_dynamic_variable!( + dynamic, "dt", mk_float; parallel_io=parallel_io, + description="current timestep size") + + io_failure_counter = create_dynamic_variable!( + dynamic, "failure_counter", mk_int; parallel_io=parallel_io, + description="cumulative number of timestep failures for the run") + + n_failure_vars = 1 + evolve_density + evolve_upar + evolve_ppar + if n_neutral_species > 0 + n_failure_vars *= 2 + end + io_failure_caused_by = create_dynamic_variable!( + dynamic, "failure_caused_by", mk_int; diagnostic_var_size=n_failure_vars, + parallel_io=parallel_io, + description="cumulative count of how many times each variable caused a " + * "timestep failure for the run") + n_limit_vars = 5 + 2 + if n_neutral_species > 0 + n_limit_vars += 2 + end + io_limit_caused_by = create_dynamic_variable!( + dynamic, "limit_caused_by", mk_int; diagnostic_var_size=n_limit_vars, + parallel_io=parallel_io, + description="cumulative count of how many times each factor limited the " + * "timestep for the run") + + io_dt_before_last_fail = create_dynamic_variable!( + dynamic, "dt_before_last_fail", mk_float; parallel_io=parallel_io, + description="Last successful timestep before most recent timestep failure, " + * "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, @@ -671,7 +718,15 @@ 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, - io_time_for_run, parallel_io) + 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) end # For processes other than the root process of each shared-memory group... @@ -839,10 +894,34 @@ function define_dynamic_ion_moment_variables!(fid, n_ion_species, r::coordinate, 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 + external_source_controller_integral, io_chodura_lower, io_chodura_upper, + ion_constraints_A_coefficient, ion_constraints_B_coefficient, + ion_constraints_C_coefficient end """ @@ -943,67 +1022,35 @@ function define_dynamic_neutral_moment_variables!(fid, n_neutral_species, r::coo external_source_neutral_controller_integral = nothing end - io_time_for_run = create_dynamic_variable!( - dynamic, "time_for_run", mk_float; parallel_io=parallel_io, - description="cumulative wall clock time for run (excluding setup)", - units="minutes") - - io_step_counter = create_dynamic_variable!( - dynamic, "step_counter", mk_int; parallel_io=parallel_io, - description="cumulative number of timesteps for the run") - - io_dt = create_dynamic_variable!( - dynamic, "dt", mk_float; parallel_io=parallel_io, - description="current timestep size") - - io_failure_counter = create_dynamic_variable!( - dynamic, "failure_counter", mk_int; parallel_io=parallel_io, - description="cumulative number of timestep failures for the run") - - n_failure_vars = 1 + evolve_density + evolve_upar + evolve_ppar - if n_neutral_species > 0 - n_failure_vars *= 2 - end - io_failure_caused_by = create_dynamic_variable!( - dynamic, "failure_caused_by", mk_int; diagnostic_var_size=n_failure_vars, - parallel_io=parallel_io, - description="cumulative count of how many times each variable caused a " - * "timestep failure for the run") - n_limit_vars = 5 + 2 - if n_neutral_species > 0 - n_limit_vars += 2 - end - io_limit_caused_by = create_dynamic_variable!( - dynamic, "limit_caused_by", mk_int; diagnostic_var_size=n_limit_vars, - parallel_io=parallel_io, - description="cumulative count of how many times each factor limited the " - * "timestep for the run") - - io_dt_before_last_fail = create_dynamic_variable!( - dynamic, "dt_before_last_fail", mk_float; parallel_io=parallel_io, - description="Last successful timestep before most recent timestep failure, " - * "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_pz_neutral, io_qz_neutral, io_thermal_speed_neutral, - external_source_amplitude, - external_source_density_amplitude, - external_source_momentum_amplitude, - external_source_pressure_amplitude, - external_source_controller_integral, - external_source_neutral_amplitude, - external_source_neutral_density_amplitude, - external_source_neutral_momentum_amplitude, - external_source_neutral_pressure_amplitude, - external_source_neutral_controller_integral, - 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) + 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 - # For processes other than the root process of each shared-memory group... - return nothing + 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 """ @@ -1169,6 +1216,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"), @@ -1271,6 +1324,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"), @@ -1328,15 +1387,26 @@ function write_all_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_or_file_info_moments, t_idx, r, z) + write_em_fields_data_to_binary(fields, io_moments, t_idx, r, z) - write_ion_moments_data_to_binary(moments, n_ion_species, io_or_file_info_moments, + 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_or_file_info_moments, t_idx, r, z) + 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 @@ -1346,27 +1416,20 @@ 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_or_file_info_moments, t_idx, r, z) +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' - if isa(io_or_file_info_moments, io_moments_info) - io_moments = io_or_file_info_moments - closefile = false - else - io_moments = reopen_moments_io(io_or_file_info_moments) - closefile = true - end - 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) - - closefile && close(io_moments.fid) end return nothing @@ -1374,20 +1437,14 @@ 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_or_file_info_moments, - t_idx, r, z) +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' - if isa(io_or_file_info_moments, io_moments_info) - io_moments = io_or_file_info_moments - closefile = false - else - io_moments = reopen_moments_io(io_or_file_info_moments) - closefile = true - end - parallel_io = io_moments.parallel_io # add the density data at this time slice to the output file @@ -1454,8 +1511,17 @@ function write_ion_moments_data_to_binary(moments, n_ion_species, io_or_file_inf t_idx, parallel_io, z, r) end end - - closefile && close(io_moments.fid) + 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 @@ -1463,9 +1529,11 @@ 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_or_file_info_moments, t_idx, r, z) + io_moments::io_moments_info, t_idx, r, z) if n_neutral_species ≤ 0 return nothing end @@ -1473,14 +1541,6 @@ function write_neutral_moments_data_to_binary(moments, n_neutral_species, @serial_region begin # Only read/write from first process in each 'block' - if isa(io_or_file_info_moments, io_moments_info) - io_moments = io_or_file_info_moments - closefile = false - else - io_moments = reopen_moments_io(io_or_file_info_moments) - closefile = true - end - parallel_io = io_moments.parallel_io append_to_dynamic_var(io_moments.density_neutral, moments.neutral.dens, t_idx, @@ -1525,19 +1585,17 @@ function write_neutral_moments_data_to_binary(moments, n_neutral_species, t_idx, parallel_io, z, r) end end - 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) + 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.neutral_constraints_B_coefficient, + moments.neutral.constraints_B_coefficient, t_idx, + parallel_io, z, r, n_neutral_species) + append_to_dynamic_var(io_moments.neutral_constraints_C_coefficient, + moments.neutral.constraints_C_coefficient, t_idx, + parallel_io, z, r, n_neutral_species) + end end return nothing @@ -1569,11 +1627,10 @@ function write_all_dfns_data_to_binary(pdf, moments, fields, t, n_ion_species, 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_or_file_info_dfns, - t_idx, r, z, vperp, vpa) - write_neutral_dfns_data_to_binary(pdf.neutral.norm, n_neutral_species, - io_or_file_info_dfns, t_idx, r, z, vzeta, vr, - vz) + 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 @@ -1582,55 +1639,39 @@ 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_or_file_info_dfns, t_idx, r, - z, vperp, vpa) +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' - if isa(io_or_file_info_dfns, io_dfns_info) - io_dfns = io_or_file_info_dfns - closefile = false - else - io_dfns = reopen_dfns_io(io_or_file_info_dfns) - closefile = true - end - parallel_io = io_dfns.parallel_io append_to_dynamic_var(io_dfns.f, ff, t_idx, parallel_io, vpa, vperp, z, r, n_ion_species) - - closefile && close(io_dfns.fid) 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_or_file_info_dfns, t_idx, r, z, vzeta, vr, + io_dfns::io_dfns_info, t_idx, r, z, vzeta, vr, vz) @serial_region begin # Only read/write from first process in each 'block' - if isa(io_or_file_info_dfns, io_dfns_info) - io_dfns = io_or_file_info_dfns - closefile = false - else - io_dfns = reopen_dfns_io(io_or_file_info_dfns) - closefile = true - end - 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 diff --git a/moment_kinetics/src/initial_conditions.jl b/moment_kinetics/src/initial_conditions.jl index da118dbad..83acac72e 100644 --- a/moment_kinetics/src/initial_conditions.jl +++ b/moment_kinetics/src/initial_conditions.jl @@ -161,6 +161,11 @@ function init_pdf_and_moments!(pdf, moments, boundary_distributions, geometry, # initialise pressures assuming isotropic distribution @. 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 # initialise the neutral density profile init_density!(moments.neutral.dens, z, r, species.neutral, n_neutral_species) @@ -176,6 +181,11 @@ function init_pdf_and_moments!(pdf, moments, boundary_distributions, geometry, @. 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 # reflect the fact that the ion moments have now been updated diff --git a/moment_kinetics/src/moment_constraints.jl b/moment_kinetics/src/moment_constraints.jl index eba1bc12a..ae49821dd 100644 --- a/moment_kinetics/src/moment_constraints.jl +++ b/moment_kinetics/src/moment_constraints.jl @@ -68,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 """ @@ -112,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 da4c3edb4..cbba0d26a 100644 --- a/moment_kinetics/src/moment_kinetics.jl +++ b/moment_kinetics/src/moment_kinetics.jl @@ -428,9 +428,9 @@ function setup_moment_kinetics(input_dict::AbstractDict; 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.ion.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_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() diff --git a/moment_kinetics/src/time_advance.jl b/moment_kinetics/src/time_advance.jl index ec76d4c28..0beb8d3f0 100644 --- a/moment_kinetics/src/time_advance.jl +++ b/moment_kinetics/src/time_advance.jl @@ -484,9 +484,15 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz 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.ion.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 @@ -505,9 +511,15 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz 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) @@ -1418,9 +1430,13 @@ function rk_update!(scratch, pdf, moments, fields, boundary_distributions, vz, v 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 @@ -1469,9 +1485,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 diff --git a/moment_kinetics/src/velocity_moments.jl b/moment_kinetics/src/velocity_moments.jl index 87c429395..af62237d7 100644 --- a/moment_kinetics/src/velocity_moments.jl +++ b/moment_kinetics/src/velocity_moments.jl @@ -126,6 +126,15 @@ struct moments_ion_substruct 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 """ @@ -209,6 +218,15 @@ struct moments_neutral_substruct 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 """ @@ -334,6 +352,16 @@ function create_moments_ion(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_ion_substruct(density, density_updated, parallel_flow, parallel_flow_updated, parallel_pressure, parallel_pressure_updated,perpendicular_pressure, @@ -343,7 +371,8 @@ function create_moments_ion(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 @@ -470,6 +499,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, @@ -477,7 +516,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 """ From d715d63d7606f58acc119d65f1e7f6905364c0a0 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 27 Apr 2024 00:38:57 +0100 Subject: [PATCH 14/42] Refactor krook_collisions, ready for adding collisions for electrons --- .../src/makie_post_processing.jl | 1 - moment_kinetics/src/fokker_planck.jl | 4 +-- moment_kinetics/src/input_structs.jl | 6 ++-- moment_kinetics/src/krook_collisions.jl | 35 +++++++++---------- moment_kinetics/src/load_data.jl | 8 ++--- moment_kinetics/src/reference_parameters.jl | 23 +++++++----- moment_kinetics/src/time_advance.jl | 14 ++++---- 7 files changed, 47 insertions(+), 44 deletions(-) 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 9377504bb..dd30ec94f 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 @@ -33,7 +33,6 @@ 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.looping: all_dimensions, ion_dimensions, neutral_dimensions using moment_kinetics.manufactured_solns: manufactured_solutions, manufactured_electric_fields diff --git a/moment_kinetics/src/fokker_planck.jl b/moment_kinetics/src/fokker_planck.jl index 52b2572ed..012183483 100644 --- a/moment_kinetics/src/fokker_planck.jl +++ b/moment_kinetics/src/fokker_planck.jl @@ -51,7 +51,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 @@ -81,7 +81,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/input_structs.jl b/moment_kinetics/src/input_structs.jl index 2a9a6a875..5262a42d8 100644 --- a/moment_kinetics/src/input_structs.jl +++ b/moment_kinetics/src/input_structs.jl @@ -91,7 +91,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 @@ -343,8 +343,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 diff --git a/moment_kinetics/src/krook_collisions.jl b/moment_kinetics/src/krook_collisions.jl index 2ef1997e4..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 @@ -99,7 +98,7 @@ function krook_collisions!(pdf_out, fvec_in, moments, composition, collisions, v @loop_s_r_z is ir iz begin n = fvec_in.density[iz,ir,is] vth = moments.ion.vth[iz,ir,is] - 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] @@ -112,7 +111,7 @@ function krook_collisions!(pdf_out, fvec_in, moments, composition, collisions, v @loop_s_r_z is ir iz begin n = fvec_in.density[iz,ir,is] vth = moments.ion.vth[iz,ir,is] - 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] @@ -125,7 +124,7 @@ function krook_collisions!(pdf_out, fvec_in, moments, composition, collisions, v @loop_s_r_z is ir iz begin n = fvec_in.density[iz,ir,is] vth = moments.ion.vth[iz,ir,is] - 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] @@ -139,7 +138,7 @@ function krook_collisions!(pdf_out, fvec_in, moments, composition, collisions, v @loop_s_r_z is ir iz begin n = fvec_in.density[iz,ir,is] vth = moments.ion.vth[iz,ir,is] - 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] @@ -157,7 +156,7 @@ function krook_collisions!(pdf_out, fvec_in, moments, composition, collisions, v 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 6b4740924..fba49c52e 100644 --- a/moment_kinetics/src/load_data.jl +++ b/moment_kinetics/src/load_data.jl @@ -20,7 +20,7 @@ using ..calculus: derivative! using ..communication: setup_distributed_memory_MPI 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 ..krook_collisions: get_collision_frequency_ii using ..input_structs: advection_input, grid_input, hdf5, netcdf using ..interpolation: interpolate_to_grid_1d! using ..looping @@ -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") @@ -3029,10 +3029,10 @@ function get_variable(run_info, variable_name; normalize_advection_speed_shape=t 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 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/time_advance.jl b/moment_kinetics/src/time_advance.jl index 0beb8d3f0..ef9bd4e6a 100644 --- a/moment_kinetics/src/time_advance.jl +++ b/moment_kinetics/src/time_advance.jl @@ -578,7 +578,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 @@ -656,8 +656,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 @@ -710,7 +710,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, @@ -1241,12 +1241,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 @@ -2106,7 +2106,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 From f0ceee4e499ed4b71d61b31af8350b3b45737a2b Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 27 Apr 2024 15:25:54 +0100 Subject: [PATCH 15/42] Setup fields, advection_structs, scratch_dummy outside setup_time_advance!() Will allow them to be passed into init_pdf_and_moments() in future to be used in kinetic electron setup. --- moment_kinetics/src/input_structs.jl | 1 + moment_kinetics/src/moment_kinetics.jl | 38 +++++++-- moment_kinetics/src/time_advance.jl | 108 +++++++++++++++---------- 3 files changed, 96 insertions(+), 51 deletions(-) diff --git a/moment_kinetics/src/input_structs.jl b/moment_kinetics/src/input_structs.jl index 5262a42d8..690776b67 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 diff --git a/moment_kinetics/src/moment_kinetics.jl b/moment_kinetics/src/moment_kinetics.jl index cbba0d26a..4039ae219 100644 --- a/moment_kinetics/src/moment_kinetics.jl +++ b/moment_kinetics/src/moment_kinetics.jl @@ -93,6 +93,9 @@ 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 .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 @@ -313,12 +316,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)), @@ -399,17 +418,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 @@ -435,7 +459,7 @@ function setup_moment_kinetics(input_dict::AbstractDict; 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/time_advance.jl b/moment_kinetics/src/time_advance.jl index ef9bd4e6a..408ed6be4 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 @@ -25,7 +26,7 @@ using ..velocity_moments: update_chodura! using ..velocity_grid_transforms: vzvrvzeta_to_vpavperp!, vpavperp_to_vzvrvzeta! using ..boundary_conditions: enforce_boundary_conditions! using ..boundary_conditions: enforce_neutral_boundary_conditions! -using ..input_structs: advance_info, time_info +using ..input_structs using ..moment_constraints: hard_force_moment_constraints!, hard_force_moment_constraints_neutral! using ..advection: setup_advection @@ -177,6 +178,54 @@ struct spectral_object_struct{Tvz,Tvr,Tvzeta,Tvpa,Tvperp,Tz,Tr} r_spectral::Tr end +function allocate_advection_structs(composition, z, r, vpa, vperp, vz, vr, vzeta) + # define some local variables for convenience/tidiness + 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 + """ create arrays and do other work needed to setup the main time advance loop. @@ -184,13 +233,14 @@ 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, +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, restarting) + manufactured_solns_input, advection_structs, scratch_dummy, + restarting) # define some local variables for convenience/tidiness n_species = composition.n_species n_ion_species = composition.n_ion_species @@ -306,8 +356,6 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz 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) @@ -317,10 +365,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) @@ -338,15 +382,17 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz 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 ## # 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() @@ -358,11 +404,6 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz # 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() @@ -373,23 +414,13 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz 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 @@ -408,9 +439,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() @@ -419,9 +447,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() @@ -434,9 +459,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 @@ -457,7 +479,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, @@ -554,9 +575,8 @@ function setup_time_advance!(pdf, vz, vr, vzeta, vpa, vperp, z, r, gyrophase, vz # 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 """ From f549a3c32f9beb6b052cc20519d8989166bfcafa Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 27 Apr 2024 15:58:26 +0100 Subject: [PATCH 16/42] Function for setup of time_info struct --- moment_kinetics/src/time_advance.jl | 88 +++++++++++++++++------------ 1 file changed, 53 insertions(+), 35 deletions(-) diff --git a/moment_kinetics/src/time_advance.jl b/moment_kinetics/src/time_advance.jl index 408ed6be4..f42e46585 100644 --- a/moment_kinetics/src/time_advance.jl +++ b/moment_kinetics/src/time_advance.jl @@ -227,26 +227,22 @@ function allocate_advection_structs(composition, z, r, vpa, vperp, vz, vr, vzeta 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 + setup_time_info(t_input; electrons=nothing) + +Create a [`input_structs.time_info`](@ref) struct using the settings in `t_input`. """ -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_species = composition.n_species - 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 - neutral_mom_diss_coeff = num_diss_params.neutral.moment_dissipation_coefficient +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) @@ -276,27 +272,49 @@ function setup_time_advance!(pdf, fields, vz, vr, vzeta, vpa, vperp, z, r, gyrop 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 From 57ba1272097d2e84b29881072764aac24e1f5604 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 27 Apr 2024 16:33:50 +0100 Subject: [PATCH 17/42] Add shared-memory scratch arrays to coordinate structs Will be useful for boundary conditions for electrons. --- moment_kinetics/src/coordinates.jl | 29 ++++++++++++++++++++++++++--- moment_kinetics/src/load_data.jl | 13 +++++++++---- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/moment_kinetics/src/coordinates.jl b/moment_kinetics/src/coordinates.jl index 995164a52..dae38e039 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/load_data.jl b/moment_kinetics/src/load_data.jl index fba49c52e..3962ddea1 100644 --- a/moment_kinetics/src/load_data.jl +++ b/moment_kinetics/src/load_data.jl @@ -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) @@ -290,7 +295,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 @@ -2492,8 +2498,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") From 7443b008af159de2ba08e3937d9b46f0ee222b3f Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 27 Apr 2024 17:34:30 +0100 Subject: [PATCH 18/42] Restore writing of ion distribution function to ascii output files --- moment_kinetics/src/file_io.jl | 15 ++++++++------- moment_kinetics/src/moment_kinetics.jl | 2 +- moment_kinetics/src/time_advance.jl | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/moment_kinetics/src/file_io.jl b/moment_kinetics/src/file_io.jl index 21d2e6977..d3275b713 100644 --- a/moment_kinetics/src/file_io.jl +++ b/moment_kinetics/src/file_io.jl @@ -38,7 +38,7 @@ 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_ion::T @@ -205,13 +205,13 @@ 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") + 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_ion_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 = io_input.run_id @@ -1710,7 +1710,7 @@ 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_ion === nothing # ascii I/O is disabled @@ -1720,7 +1720,7 @@ 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(pdf, z, vpa, t, ascii_io.ff) + write_f_ascii(pdf.ion.norm, 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) @@ -1812,7 +1812,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 diff --git a/moment_kinetics/src/moment_kinetics.jl b/moment_kinetics/src/moment_kinetics.jl index 4039ae219..aa18d69ee 100644 --- a/moment_kinetics/src/moment_kinetics.jl +++ b/moment_kinetics/src/moment_kinetics.jl @@ -445,7 +445,7 @@ 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 diff --git a/moment_kinetics/src/time_advance.jl b/moment_kinetics/src/time_advance.jl index f42e46585..8a4145e4c 100644 --- a/moment_kinetics/src/time_advance.jl +++ b/moment_kinetics/src/time_advance.jl @@ -1112,7 +1112,7 @@ 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_all_moments_data_to_binary(moments, fields, t, From 88e3854bd0daf2c7bae755d5732fdc6ef10e9dd2 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 27 Apr 2024 18:16:47 +0100 Subject: [PATCH 19/42] Allow saving/loading when some coordinates are not present Allow passing or returning `nothing` if a coordinate is not present. --- .../src/makie_post_processing.jl | 35 +++++++++-- moment_kinetics/src/file_io.jl | 62 ++++++++++++------- moment_kinetics/src/load_data.jl | 29 ++++++--- 3 files changed, 88 insertions(+), 38 deletions(-) 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 dd30ec94f..0eb6ab6ea 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 @@ -542,11 +542,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 diff --git a/moment_kinetics/src/file_io.jl b/moment_kinetics/src/file_io.jl index d3275b713..a172f86b8 100644 --- a/moment_kinetics/src/file_io.jl +++ b/moment_kinetics/src/file_io.jl @@ -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 diff --git a/moment_kinetics/src/load_data.jl b/moment_kinetics/src/load_data.jl index 3962ddea1..b522ee4ec 100644 --- a/moment_kinetics/src/load_data.jl +++ b/moment_kinetics/src/load_data.jl @@ -214,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") @@ -652,14 +657,18 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, # 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 @@ -674,7 +683,9 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, if parallel_io function get_range(coord) - if coord.irank == coord.nrank - 1 + 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 @@ -2642,7 +2653,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 @@ -2656,7 +2667,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 @@ -2670,7 +2681,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 @@ -2684,7 +2695,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 @@ -2698,7 +2709,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 From 0f7c6d6b812a48fbc4e5ee70c21ab0f5694248d6 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Mon, 19 Feb 2024 13:43:37 +0000 Subject: [PATCH 20/42] Refactor reloading functions Define reload_moments(), etc., functions at the module level instead of locally inside reload_evolving_fields!(). This will allow (some of) these functions to be used separately to reload electron variables. --- moment_kinetics/src/load_data.jl | 2539 ++++++++++++++++-------------- 1 file changed, 1331 insertions(+), 1208 deletions(-) diff --git a/moment_kinetics/src/load_data.jl b/moment_kinetics/src/load_data.jl index b522ee4ec..a98283603 100644 --- a/moment_kinetics/src/load_data.jl +++ b/moment_kinetics/src/load_data.jl @@ -585,75 +585,11 @@ 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( @@ -681,69 +617,34 @@ 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 === 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 - - 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 - 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 + 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 .= load_moment("density") + 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 .= load_moment("parallel_flow") + 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 .= load_moment("parallel_pressure") + 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 .= load_moment("parallel_heat_flux") + 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 .= load_moment("thermal_speed") + 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) if z.irank == 0 if "chodura_integral_lower" ∈ keys(dynamic) moments.ion.chodura_integral_lower .= load_slice(dynamic, "chodura_integral_lower", @@ -767,530 +668,71 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, load_slice(dynamic, "external_source_controller_integral", time_index) elseif length(moments.ion.external_source_controller_integral) > 1 moments.ion.external_source_controller_integral .= - load_moment("external_source_controller_integral") - end - - function load_ion_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.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 - @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 + 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 - pdf.ion.norm .= load_ion_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_ion_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.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 - boundary_distributions.pdf_rboundary_ion[:,:,:,1,:] .= - load_ion_boundary_pdf("pdf_rboundary_ion_left", 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,:] .= - load_ion_boundary_pdf("pdf_rboundary_ion_right", r.n) + 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 "external_source_neutral_controller_integral" ∈ get_variable_keys(dynamic) && length(moments.neutral.external_source_controller_integral) == 1 @@ -1300,579 +742,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) @@ -1887,7 +796,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) @@ -1897,6 +806,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 From f7eb2e6d0460f284e3d2dcefebdc1247515d650e Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 27 Apr 2024 19:01:03 +0100 Subject: [PATCH 21/42] Refactor restart - move functionality to functions in utils module --- moment_kinetics/src/moment_kinetics.jl | 119 +---------------- moment_kinetics/src/utils.jl | 169 +++++++++++++++++++++++++ 2 files changed, 174 insertions(+), 114 deletions(-) diff --git a/moment_kinetics/src/moment_kinetics.jl b/moment_kinetics/src/moment_kinetics.jl index aa18d69ee..5e01d0885 100644 --- a/moment_kinetics/src/moment_kinetics.jl +++ b/moment_kinetics/src/moment_kinetics.jl @@ -92,7 +92,8 @@ 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 @@ -194,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. @@ -354,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 = 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 """ From 825e438789147c043cb8f9efce4a8a500fc4a2e4 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Mon, 25 Mar 2024 16:35:53 +0000 Subject: [PATCH 22/42] Fix typo in external_sources docs --- docs/src/external_sources_notes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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} ``` From 8def3669b5dabb24c7180bb42724ea11e2190e67 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Mon, 25 Mar 2024 17:48:11 +0000 Subject: [PATCH 23/42] Fix source moments to be compatible with source_type="energy_source" Previously, the moments of the source term were introduced in vpa/vz 'advection speed' and in source_terms in terms of `external_source_amplitude`. The moments were taken assuming that the source is a Maxwellian with temperature `T_source`. This is not true for "energy_source" (where the source is defined as roughly 'f_M - f' in such a way that no particles are introduced). However, the moments of the source term are already calculated in external_density_source and external_pressure_source, so these can be used instead to support more generic source terms. --- moment_kinetics/src/load_data.jl | 22 ++++++++++++++-- moment_kinetics/src/neutral_vz_advection.jl | 19 +++++++------- moment_kinetics/src/source_terms.jl | 28 ++++++++++++--------- moment_kinetics/src/vpa_advection.jl | 19 +++++++------- 4 files changed, 56 insertions(+), 32 deletions(-) diff --git a/moment_kinetics/src/load_data.jl b/moment_kinetics/src/load_data.jl index a98283603..561f78aed 100644 --- a/moment_kinetics/src/load_data.jl +++ b/moment_kinetics/src/load_data.jl @@ -3264,8 +3264,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) @@ -3288,7 +3294,10 @@ function get_variable(run_info, variable_name; normalize_advection_speed_shape=t dvth_dz=dvth_dz[:,:,:,it], dqpar_dz=dqpar_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) @@ -3382,8 +3391,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) @@ -3412,7 +3427,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) 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/source_terms.jl b/moment_kinetics/src/source_terms.jl index 790603f34..9850d6f23 100644 --- a/moment_kinetics/src/source_terms.jl +++ b/moment_kinetics/src/source_terms.jl @@ -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.ion.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.ion.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 diff --git a/moment_kinetics/src/vpa_advection.jl b/moment_kinetics/src/vpa_advection.jl index 3faed5ba4..9d4881b0b 100644 --- a/moment_kinetics/src/vpa_advection.jl +++ b/moment_kinetics/src/vpa_advection.jl @@ -150,19 +150,21 @@ function update_speed_n_u_p_evolution!(advect, fvec, moments, vpa, z, r, composi end end if ion_source_settings.active - source_amplitude = moments.ion.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.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 @@ -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.ion.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.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 From 0f0e41cd025bfdc5b32732120f458189aeead47a Mon Sep 17 00:00:00 2001 From: John Omotani Date: Wed, 27 Mar 2024 22:13:10 +0000 Subject: [PATCH 24/42] Fix typo in docs for source term in neutral moment-kinetic equation The corrected form is similar to the ion one, and has correct dimensions. --- docs/src/moment_kinetic_equations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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}} \\ From da7ff5792d6c4264c6ca2aa32e81fe2de92fbe4e Mon Sep 17 00:00:00 2001 From: John Omotani Date: Tue, 9 Apr 2024 19:32:03 +0100 Subject: [PATCH 25/42] Add 'git' module in setup for Marconi --- machines/marconi/julia.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 313d39c93af15c7a5a7f719263a54fd937a3404d Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 24 Feb 2024 21:08:45 +0000 Subject: [PATCH 26/42] Support selection by `it` for the time variable in title of animations --- .../src/makie_post_processing.jl | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) 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 0eb6ab6ea..27fde672c 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 @@ -1843,9 +1843,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 @@ -1933,7 +1935,8 @@ 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...) @@ -2078,10 +2081,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) @@ -2164,6 +2169,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) From b68ada72d2dee7a9e21e8e9211b0532a9574608f Mon Sep 17 00:00:00 2001 From: John Omotani Date: Mon, 11 Mar 2024 13:16:17 +0000 Subject: [PATCH 27/42] Fix formatting of docstrings for plot_vs_* and animate_vs_* functions --- .../src/makie_post_processing.jl | 110 +++++++++--------- 1 file changed, 55 insertions(+), 55 deletions(-) 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 27fde672c..309aa1851 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 @@ -1305,7 +1305,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 ) @@ -1317,17 +1317,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). @@ -1523,7 +1523,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 @@ -1538,19 +1538,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). @@ -1730,7 +1730,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) @@ -1738,19 +1738,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, + $($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). @@ -1986,7 +1986,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 ) @@ -1997,20 +1997,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). From 7fcf37fc5098950ca5d7c916cbc4c10150319c05 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Mon, 11 Mar 2024 13:20:35 +0000 Subject: [PATCH 28/42] Allow passing a `label` kwarg to 1d animation functions --- .../src/makie_post_processing.jl | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) 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 309aa1851..14b9ad42e 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 @@ -1747,7 +1747,7 @@ for dim ∈ one_dimension_combinations_no_t $($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)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...) @@ -1782,6 +1782,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 @@ -1893,10 +1896,10 @@ 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) @@ -1943,13 +1946,16 @@ for dim ∈ one_dimension_combinations_no_t 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 = From 19a0bfcf7164980052ce9cdae1d43ba8902d0296 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Mon, 11 Mar 2024 14:28:35 +0000 Subject: [PATCH 29/42] Make plots of coefficients for moment constraints --- .../makie_post_processing/src/makie_post_processing.jl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 14b9ad42e..c109a5625 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,9 +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.input_structs using moment_kinetics.looping: all_dimensions, ion_dimensions, neutral_dimensions using moment_kinetics.manufactured_solns: manufactured_solutions, manufactured_electric_fields From 538d1df2471542a3fe6e6ecabd4134740c5b9ae7 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 23 Mar 2024 17:36:59 +0000 Subject: [PATCH 30/42] Fix lookup of dfn vars from makie_post_processing input Fixes changes from 399e521acca84d50d5635affa004223bcc6128b4. --- .../src/makie_post_processing.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 c109a5625..76ba7c91b 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 @@ -1390,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 @@ -1452,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 @@ -1637,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 @@ -1820,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 @@ -1901,7 +1901,7 @@ for dim ∈ one_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 @@ -2131,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 From 45b3afff0c915b44b073bc059aff24111e21cffe Mon Sep 17 00:00:00 2001 From: John Omotani Date: Mon, 25 Mar 2024 14:08:21 +0000 Subject: [PATCH 31/42] Support `it` argument for CFL animations in `timestep_diagnostics()` --- .../makie_post_processing/src/makie_post_processing.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 76ba7c91b..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 @@ -7127,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), @@ -7138,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), @@ -7150,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), @@ -7161,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), From 442b3ae32b22f0d5f20251a551a9cdcba0a495ea Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 27 Apr 2024 21:38:03 +0100 Subject: [PATCH 32/42] Simplify arguments to define_dynamic_dfn_variables!() --- moment_kinetics/src/file_io.jl | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/moment_kinetics/src/file_io.jl b/moment_kinetics/src/file_io.jl index a172f86b8..a425a93bc 100644 --- a/moment_kinetics/src/file_io.jl +++ b/moment_kinetics/src/file_io.jl @@ -1071,14 +1071,13 @@ 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, @@ -1088,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="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") @@ -1288,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) From 421b63825603bfa4deb959aea8a0badc04264e60 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 27 Apr 2024 22:04:59 +0100 Subject: [PATCH 33/42] Move moments and pdf structs to moment_kinetics_structs --- moment_kinetics/src/initial_conditions.jl | 45 +--- .../src/moment_kinetics_structs.jl | 229 ++++++++++++++++++ moment_kinetics/src/velocity_moments.jl | 186 +------------- 3 files changed, 234 insertions(+), 226 deletions(-) diff --git a/moment_kinetics/src/initial_conditions.jl b/moment_kinetics/src/initial_conditions.jl index 83acac72e..bc82f058d 100644 --- a/moment_kinetics/src/initial_conditions.jl +++ b/moment_kinetics/src/initial_conditions.jl @@ -20,11 +20,11 @@ using ..communication 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_vz, integrate_over_negative_vz using ..velocity_moments: create_moments_ion, create_moments_neutral, update_qpar! -using ..velocity_moments: moments_ion_substruct, moments_neutral_substruct 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! @@ -33,47 +33,6 @@ 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 - #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 - """ Creates the structs for the pdf and the velocity-space moments """ diff --git a/moment_kinetics/src/moment_kinetics_structs.jl b/moment_kinetics/src/moment_kinetics_structs.jl index 6efe78dcd..5ac80b23c 100644 --- a/moment_kinetics/src/moment_kinetics_structs.jl +++ b/moment_kinetics/src/moment_kinetics_structs.jl @@ -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/velocity_moments.jl b/moment_kinetics/src/velocity_moments.jl index af62237d7..a0c7388b3 100644 --- a/moment_kinetics/src/velocity_moments.jl +++ b/moment_kinetics/src/velocity_moments.jl @@ -14,7 +14,6 @@ export update_pperp! export update_qpar! export update_vth! export reset_moments_status! -export moments_ion_substruct, moments_neutral_substruct export update_neutral_density! export update_neutral_uz! export update_neutral_ur! @@ -42,193 +41,14 @@ 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) #global n_hist = 0 -""" -""" -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 - """ """ function create_moments_ion(nz, nr, n_species, evolve_density, evolve_upar, From 35fa061c6b770ca450526b6b0fe557de3fc425ad Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sun, 7 Apr 2024 09:49:58 +0100 Subject: [PATCH 34/42] Reload *_constraints_*_coefficient, to avoid using uninitialized values --- moment_kinetics/src/load_data.jl | 62 ++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/moment_kinetics/src/load_data.jl b/moment_kinetics/src/load_data.jl index 561f78aed..69ebe3d3a 100644 --- a/moment_kinetics/src/load_data.jl +++ b/moment_kinetics/src/load_data.jl @@ -645,6 +645,39 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, 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 + 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 "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 + end if z.irank == 0 if "chodura_integral_lower" ∈ keys(dynamic) moments.ion.chodura_integral_lower .= load_slice(dynamic, "chodura_integral_lower", @@ -733,6 +766,35 @@ function reload_evolving_fields!(pdf, moments, boundary_distributions, 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 From 0fee79f83a60ebcf10387d3d4b50bea79c985bcf Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 27 Apr 2024 22:48:42 +0100 Subject: [PATCH 35/42] Type check on coords argument of write_single_value!() --- moment_kinetics/src/file_io_hdf5.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 From 46fb53c8e5da95456638149451b2daaf5fb309d3 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 27 Apr 2024 22:53:50 +0100 Subject: [PATCH 36/42] Simplify imports in load_data --- moment_kinetics/src/load_data.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/moment_kinetics/src/load_data.jl b/moment_kinetics/src/load_data.jl index 69ebe3d3a..f1ff32488 100644 --- a/moment_kinetics/src/load_data.jl +++ b/moment_kinetics/src/load_data.jl @@ -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_ii -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! From 7971ba572b96e04910f3806851fc6451137266ab Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 27 Apr 2024 22:56:19 +0100 Subject: [PATCH 37/42] Add missing 'return' for extended_moments in load_ion_moments_data() --- moment_kinetics/src/load_data.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moment_kinetics/src/load_data.jl b/moment_kinetics/src/load_data.jl index f1ff32488..46b191ad6 100644 --- a/moment_kinetics/src/load_data.jl +++ b/moment_kinetics/src/load_data.jl @@ -486,7 +486,7 @@ function load_ion_moments_data(fid; printout=false, extended_moments = false) 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 From 0c25142a5039aae0a790ab652814b2c0b3b41ae1 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 27 Apr 2024 23:07:48 +0100 Subject: [PATCH 38/42] Simplify getting 'per step' values from 'cumulative' Also avoid division by zero - in a hacky way by just setting the result to zero if we would have divided by zero. --- moment_kinetics/src/load_data.jl | 50 +++++++++++++++++++------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/moment_kinetics/src/load_data.jl b/moment_kinetics/src/load_data.jl index 46b191ad6..91992e9c6 100644 --- a/moment_kinetics/src/load_data.jl +++ b/moment_kinetics/src/load_data.jl @@ -3227,6 +3227,27 @@ 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 @@ -3506,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...) @@ -3540,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 From 16d46abe07f121df369440e688d0c6bd6316d37c Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 27 Apr 2024 23:25:59 +0100 Subject: [PATCH 39/42] deepcopy() inputs in tests to ensure they are not accidentally modified --- moment_kinetics/test/Krook_collisions_tests.jl | 4 ++++ moment_kinetics/test/fokker_planck_time_evolution_tests.jl | 4 ++++ moment_kinetics/test/harrisonthompson.jl | 4 ++++ moment_kinetics/test/nonlinear_sound_wave_tests.jl | 4 ++++ moment_kinetics/test/recycling_fraction_tests.jl | 4 ++++ moment_kinetics/test/restart_interpolation_tests.jl | 4 ++++ moment_kinetics/test/sound_wave_tests.jl | 4 ++++ moment_kinetics/test/wall_bc_tests.jl | 4 ++++ 8 files changed, 32 insertions(+) diff --git a/moment_kinetics/test/Krook_collisions_tests.jl b/moment_kinetics/test/Krook_collisions_tests.jl index 2e6a2dc5e..91e6bb7de 100644 --- a/moment_kinetics/test/Krook_collisions_tests.jl +++ b/moment_kinetics/test/Krook_collisions_tests.jl @@ -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 diff --git a/moment_kinetics/test/fokker_planck_time_evolution_tests.jl b/moment_kinetics/test/fokker_planck_time_evolution_tests.jl index d93d427ee..3039b934d 100644 --- a/moment_kinetics/test/fokker_planck_time_evolution_tests.jl +++ b/moment_kinetics/test/fokker_planck_time_evolution_tests.jl @@ -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 diff --git a/moment_kinetics/test/harrisonthompson.jl b/moment_kinetics/test/harrisonthompson.jl index 5f9a05164..553515a19 100644 --- a/moment_kinetics/test/harrisonthompson.jl +++ b/moment_kinetics/test/harrisonthompson.jl @@ -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/nonlinear_sound_wave_tests.jl b/moment_kinetics/test/nonlinear_sound_wave_tests.jl index f6e5d7334..0bb965494 100644 --- a/moment_kinetics/test/nonlinear_sound_wave_tests.jl +++ b/moment_kinetics/test/nonlinear_sound_wave_tests.jl @@ -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 diff --git a/moment_kinetics/test/recycling_fraction_tests.jl b/moment_kinetics/test/recycling_fraction_tests.jl index aaca8d80b..9e3c1697d 100644 --- a/moment_kinetics/test/recycling_fraction_tests.jl +++ b/moment_kinetics/test/recycling_fraction_tests.jl @@ -167,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 c4d19e288..9c0a1c99b 100644 --- a/moment_kinetics/test/restart_interpolation_tests.jl +++ b/moment_kinetics/test/restart_interpolation_tests.jl @@ -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 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/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 From f8b629b90123c87437ba01354b593b13266940b1 Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sat, 27 Apr 2024 23:27:39 +0100 Subject: [PATCH 40/42] Add comments about using ignore_MPI=true argument in tests --- moment_kinetics/test/calculus_tests.jl | 30 +++++++++++++++++++ moment_kinetics/test/interpolation_tests.jl | 2 ++ .../test/velocity_integral_tests.jl | 2 ++ 3 files changed, 34 insertions(+) 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/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/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) From 780ce0b5f14243f87db8443ad811fe993db836bb Mon Sep 17 00:00:00 2001 From: John Omotani Date: Tue, 2 Apr 2024 08:51:09 +0100 Subject: [PATCH 41/42] Allow `nstep = 0` Lets code be run just to write out the initial profiles. --- moment_kinetics/src/time_advance.jl | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/moment_kinetics/src/time_advance.jl b/moment_kinetics/src/time_advance.jl index 8a4145e4c..157257955 100644 --- a/moment_kinetics/src/time_advance.jl +++ b/moment_kinetics/src/time_advance.jl @@ -262,13 +262,21 @@ function setup_time_info(t_input, code_time, dt_reload, dt_before_last_fail_relo 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 @@ -988,6 +996,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 From 7d3a3f7c79de81c956ba34831df2efd8af215b1e Mon Sep 17 00:00:00 2001 From: John Omotani Date: Sun, 28 Apr 2024 10:44:22 +0100 Subject: [PATCH 42/42] Fix ascii I/O for the ion distribution function --- moment_kinetics/src/file_io.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moment_kinetics/src/file_io.jl b/moment_kinetics/src/file_io.jl index a425a93bc..bae07d9bc 100644 --- a/moment_kinetics/src/file_io.jl +++ b/moment_kinetics/src/file_io.jl @@ -1732,7 +1732,7 @@ function write_data_to_ascii(pdf, moments, fields, vpa, vperp, z, r, t, n_ion_sp @serial_region begin # Only read/write from first process in each 'block' - write_f_ascii(pdf.ion.norm, z, vpa, t, ascii_io.ff) + 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)